﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ParserGenerator.Collections;
using ParserGenerator.Extensions;

namespace ParserGenerator.SyntacticAnalysis
{
    [Serializable]
    internal sealed class SyntaxTreeNodeTypeReducer
    {
        private readonly GrammarDeclaration _grammar;
        private readonly Identifier _syntaxTreeNodeWriterInterfaceName;
        private readonly Identifier _syntaxTreeNodeWriterClassName;
        private readonly Identifier _syntaxTreeNodeWalkerClassName;
        private readonly ReadOnlySet<string> _valueTypes;
        private readonly ReadOnlySet<string> _nodeTypes;
        private readonly ReadOnlyDictionary<string, Sequence<string>> _derivedTypes;
        private readonly ReadOnlyDictionary<string, ReadOnlyCollection<VisitorDeclarator>> _referenceVisitors;
        private readonly ReadOnlyDictionary<string, Sequence<StructFieldDeclarator>> _childNodes;
        private readonly Sequence<TypeDeclarator> _nodeTypeDeclarators;

        public GrammarDeclaration Grammar => _grammar;
        public ReadOnlySet<string> ValueTypes => _valueTypes;
        public ReadOnlyDictionary<string, Sequence<string>> DerivedTypes => _derivedTypes;
        public Sequence<TypeDeclarator> NodeTypeDeclarators => _nodeTypeDeclarators;

        public SyntaxTreeNodeTypeReducer(GrammarDeclaration grammar)
        {
            if (grammar == null)
                throw new ArgumentNullException(nameof(grammar));
            _grammar = grammar;
            _syntaxTreeNodeWriterInterfaceName = GetSyntaxTreeNodeWriterInterfaceName();
            _syntaxTreeNodeWriterClassName = GetSyntaxTreeNodeWriterClassName();
            _syntaxTreeNodeWalkerClassName = GetSyntaxTreeNodeWalkerClassName();
            _valueTypes = GetSyntaxNodeValueTypes();
            _nodeTypes = GetSyntaxNodeTypes();
            _derivedTypes = GetDerivedTypes();
            _referenceVisitors = GetReferenceVisitors();
            _childNodes = GetChildNodes();
            _nodeTypeDeclarators = ConvertSyntaxNodeTypes();
        }

        #region TypeCheck
        public bool IsValueType(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            switch (n.Tag)
            {
                case TypeExpressionCategory.Int8:
                case TypeExpressionCategory.Int16:
                case TypeExpressionCategory.Int32:
                case TypeExpressionCategory.Int64:

                case TypeExpressionCategory.UInt8:
                case TypeExpressionCategory.UInt16:
                case TypeExpressionCategory.UInt32:
                case TypeExpressionCategory.UInt64:

                case TypeExpressionCategory.Float32:
                case TypeExpressionCategory.Float64:

                case TypeExpressionCategory.Char8:
                case TypeExpressionCategory.Char16:
                case TypeExpressionCategory.Char32:

                case TypeExpressionCategory.SByte:
                case TypeExpressionCategory.Short:
                case TypeExpressionCategory.Int:
                case TypeExpressionCategory.Long:

                case TypeExpressionCategory.Byte:
                case TypeExpressionCategory.UShort:
                case TypeExpressionCategory.UInt:
                case TypeExpressionCategory.ULong:

                case TypeExpressionCategory.IntPtrT:
                case TypeExpressionCategory.UIntPtrT:
                case TypeExpressionCategory.SizeT:
                case TypeExpressionCategory.SSizeT:
                case TypeExpressionCategory.PtrDiffT:

                case TypeExpressionCategory.Float:
                case TypeExpressionCategory.Double:
                case TypeExpressionCategory.Decimal:

                case TypeExpressionCategory.Bool:
                case TypeExpressionCategory.Char:
                    return true;

                case TypeExpressionCategory.String:
                case TypeExpressionCategory.Object:
                case TypeExpressionCategory.Dynamic:
                    return false;

                case TypeExpressionCategory.Unit:
                    return false;

                case TypeExpressionCategory.Tuple:
                case TypeExpressionCategory.Record:
                    return false;
                case TypeExpressionCategory.ValueTuple:
                case TypeExpressionCategory.ValueRecord:
                    return true;

                case TypeExpressionCategory.List:
                case TypeExpressionCategory.Set:
                case TypeExpressionCategory.Map:
                    return false;

                case TypeExpressionCategory.NameReference:
                    return _valueTypes.Contains(n.AsNameReference.Name.Name);
                case TypeExpressionCategory.GlobalNameReference:
                    return false;

                case TypeExpressionCategory.Access:
                    return _valueTypes.Contains(n.AsAccess.Member.Name);
                case TypeExpressionCategory.UnboundedGeneric:
                case TypeExpressionCategory.BoundedGeneric:
                    return false;

                case TypeExpressionCategory.NonNegativeInt8:
                case TypeExpressionCategory.NonNegativeInt16:
                case TypeExpressionCategory.NonNegativeInt32:
                case TypeExpressionCategory.NonNegativeInt64:

                case TypeExpressionCategory.NonNegativeFloat32:
                case TypeExpressionCategory.NonNegativeFloat64:

                case TypeExpressionCategory.NonNegativeSByte:
                case TypeExpressionCategory.NonNegativeShort:
                case TypeExpressionCategory.NonNegativeInt:
                case TypeExpressionCategory.NonNegativeLong:

                case TypeExpressionCategory.NonNegativeIntPtrT:
                case TypeExpressionCategory.NonNegativeSSizeT:
                case TypeExpressionCategory.NonNegativePtrDiffT:

                case TypeExpressionCategory.NonNegativeFloat:
                case TypeExpressionCategory.NonNegativeDouble:
                case TypeExpressionCategory.NonNegativeDecimal:

                case TypeExpressionCategory.PositiveInt8:
                case TypeExpressionCategory.PositiveInt16:
                case TypeExpressionCategory.PositiveInt32:
                case TypeExpressionCategory.PositiveInt64:

                case TypeExpressionCategory.PositiveFloat32:
                case TypeExpressionCategory.PositiveFloat64:

                case TypeExpressionCategory.PositiveSByte:
                case TypeExpressionCategory.PositiveShort:
                case TypeExpressionCategory.PositiveInt:
                case TypeExpressionCategory.PositiveLong:

                case TypeExpressionCategory.PositiveIntPtrT:
                case TypeExpressionCategory.PositiveSSizeT:
                case TypeExpressionCategory.PositiveDiffT:

                case TypeExpressionCategory.PositiveFloat:
                case TypeExpressionCategory.PositiveDouble:
                case TypeExpressionCategory.PositiveDecimal:
                    return true;

                case TypeExpressionCategory.MutableList:
                case TypeExpressionCategory.MutableSet:
                case TypeExpressionCategory.MutableMap:

                case TypeExpressionCategory.NonEmptyList:
                case TypeExpressionCategory.NonEmptySet:
                case TypeExpressionCategory.NonEmptyMap:

                case TypeExpressionCategory.NonEmptyMutableList:
                case TypeExpressionCategory.NonEmptyMutableSet:
                case TypeExpressionCategory.NonEmptyMutableMap:
                    return false;

                case TypeExpressionCategory.Nullable:
                case TypeExpressionCategory.Union:
                case TypeExpressionCategory.Function:
                    return false;

                default:
                    throw new ArgumentException(SR.InvalidTypeExpressionCategory, nameof(n));
            }
        }

        private bool IsPositiveIntegerType(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            switch (n.Tag)
            {
                case TypeExpressionCategory.PositiveInt8:
                case TypeExpressionCategory.PositiveInt16:
                case TypeExpressionCategory.PositiveInt32:
                case TypeExpressionCategory.PositiveInt64:

                case TypeExpressionCategory.PositiveFloat32:
                case TypeExpressionCategory.PositiveFloat64:

                case TypeExpressionCategory.PositiveSByte:
                case TypeExpressionCategory.PositiveShort:
                case TypeExpressionCategory.PositiveInt:
                case TypeExpressionCategory.PositiveLong:

                case TypeExpressionCategory.PositiveIntPtrT:
                case TypeExpressionCategory.PositiveSSizeT:
                case TypeExpressionCategory.PositiveDiffT:

                case TypeExpressionCategory.PositiveFloat:
                case TypeExpressionCategory.PositiveDouble:
                case TypeExpressionCategory.PositiveDecimal:
                    return true;

                default:
                    return false;
            }
        }

        private bool IsNativeIntegerType(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            switch (n.Tag)
            {
                case TypeExpressionCategory.IntPtrT:
                case TypeExpressionCategory.UIntPtrT:
                case TypeExpressionCategory.SizeT:
                case TypeExpressionCategory.SSizeT:
                case TypeExpressionCategory.PtrDiffT:

                case TypeExpressionCategory.NonNegativeIntPtrT:
                case TypeExpressionCategory.NonNegativeSSizeT:
                case TypeExpressionCategory.NonNegativePtrDiffT:

                case TypeExpressionCategory.PositiveIntPtrT:
                case TypeExpressionCategory.PositiveSSizeT:
                case TypeExpressionCategory.PositiveDiffT:
                    return true;

                default:
                    return false;
            }
        }
        #endregion

        #region NeedArgumentCheck
        private bool NeedArgumentNullCheck(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.IsNullable)
                return false;
            else
                return !IsValueType(n);
        }

        private bool NeedArgumentSequenceEmptyCheck(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            switch (n.Tag)
            {
                case TypeExpressionCategory.NonEmptyList:
                case TypeExpressionCategory.NonEmptySet:
                case TypeExpressionCategory.NonEmptyMap:

                case TypeExpressionCategory.NonEmptyMutableList:
                case TypeExpressionCategory.NonEmptyMutableSet:
                case TypeExpressionCategory.NonEmptyMutableMap:
                    return true;

                default:
                    return false;
            }
        }

        private bool NeedArgumentNumberRangeCheck(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            switch (n.Tag)
            {
                case TypeExpressionCategory.NonNegativeInt8:
                case TypeExpressionCategory.NonNegativeInt16:
                case TypeExpressionCategory.NonNegativeInt32:
                case TypeExpressionCategory.NonNegativeInt64:

                case TypeExpressionCategory.NonNegativeFloat32:
                case TypeExpressionCategory.NonNegativeFloat64:

                case TypeExpressionCategory.NonNegativeSByte:
                case TypeExpressionCategory.NonNegativeShort:
                case TypeExpressionCategory.NonNegativeInt:
                case TypeExpressionCategory.NonNegativeLong:

                case TypeExpressionCategory.NonNegativeIntPtrT:
                case TypeExpressionCategory.NonNegativeSSizeT:
                case TypeExpressionCategory.NonNegativePtrDiffT:

                case TypeExpressionCategory.NonNegativeFloat:
                case TypeExpressionCategory.NonNegativeDouble:
                case TypeExpressionCategory.NonNegativeDecimal:

                case TypeExpressionCategory.PositiveInt8:
                case TypeExpressionCategory.PositiveInt16:
                case TypeExpressionCategory.PositiveInt32:
                case TypeExpressionCategory.PositiveInt64:

                case TypeExpressionCategory.PositiveFloat32:
                case TypeExpressionCategory.PositiveFloat64:

                case TypeExpressionCategory.PositiveSByte:
                case TypeExpressionCategory.PositiveShort:
                case TypeExpressionCategory.PositiveInt:
                case TypeExpressionCategory.PositiveLong:

                case TypeExpressionCategory.PositiveIntPtrT:
                case TypeExpressionCategory.PositiveSSizeT:
                case TypeExpressionCategory.PositiveDiffT:

                case TypeExpressionCategory.PositiveFloat:
                case TypeExpressionCategory.PositiveDouble:
                case TypeExpressionCategory.PositiveDecimal:
                    return true;

                default:
                    return false;
            }
        }

        private bool NeedArgumentCheck(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (NeedArgumentNullCheck(n))
                return true;
            if (NeedArgumentSequenceEmptyCheck(n))
                return true;
            if (NeedArgumentNumberRangeCheck(n))
                return true;
            if (!n.IsNullable)
                return false;
            TypeExpression m = n.AsNullable.OperandType;
            if (NeedArgumentSequenceEmptyCheck(m))
                return true;
            if (NeedArgumentNumberRangeCheck(m))
                return true;
            return false;
        }
        #endregion

        #region GenerateArgumentCheck
        private Sequence<Statement> GenerateArgumentNullCheck(Parameter n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var arg = Expression.NewNameReference(n.Name);
            var nul = Expression.NewNull(NullLiteral.New());
            var pair = RelationalOpValuePair.New(RelationalOp.EqualTo, nul);
            var comp = Expression.NewRelational(arg, Sequence.Create(pair));
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("ArgumentNullException"));
            var nameOf = Expression.NewNameOf(arg);
            var excArg = Argument.NewPositional(nameOf);
            var excArgs = ArgumentList.New(Sequence.Create(excArg));
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var elseIfs = Sequence.GetEmpty<ElseIfBranch>();
            var ifStmt = Statement.NewIf(comp, throwStmt, elseIfs);
            return Sequence.Create(ifStmt);
        }

        private Sequence<Statement> GenerateArgumentSequenceEmptyCheck(Parameter n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var arg = Expression.NewNameReference(n.Name);
            Expression count;
            if (n.Type.IsNullable)
                count = Expression.NewNullableAccess(arg, Identifier.New("Count"));
            else
                count = Expression.NewAccess(arg, Identifier.New("Count"));
            var zero = Expression.NewInteger(IntegerLiteral.New(0));
            var pair = RelationalOpValuePair.New(RelationalOp.LessEqual, zero);
            var comp = Expression.NewRelational(count, Sequence.Create(pair));
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("ArgumentException"));
            var nameOf = Expression.NewNameOf(arg);
            var str = TypeExpression.NewString();
            var strRef = Expression.NewBuiltinTypeReference(str);
            var strFormat = Expression.NewAccess(strRef, Identifier.New("Format"));
            Expression format;
            if (_grammar.UseAssemblyStringResources)
            {
                const string id = "ArgumentMustNotBeEmptySequence";
                var srRef = Expression.NewTypeReference(_grammar.StringResourcesClassReference);
                format = Expression.NewAccess(srRef, Identifier.New(id));
            }
            else
            {
                const string msg = "\"Argument '{0}' must not be empty sequence.\"";
                format = Expression.NewString(StringLiteral.New(msg));
            }
            var callArg1 = Argument.NewPositional(format);
            var callArg2 = Argument.NewPositional(nameOf);
            var callArgs = ArgumentList.New(Sequence.Create(callArg1, callArg2));
            var call = Expression.NewCall(strFormat, callArgs);
            var excArg1 = Argument.NewPositional(call);
            var excArg2 = Argument.NewPositional(nameOf);
            var excArgs = ArgumentList.New(Sequence.Create(excArg1, excArg2));
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var elseIfs = Sequence.GetEmpty<ElseIfBranch>();
            var ifStmt = Statement.NewIf(comp, throwStmt, elseIfs);
            return Sequence.Create(ifStmt);
        }

        private Expression NumberArgumentToComparable(TypeExpression n, Expression node)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (node == null)
                throw new ArgumentNullException(nameof(node));
            if (IsNativeIntegerType(n))
            {
                Expression access = Expression.NewAccess(node, Identifier.New("ToInt64"));
                Expression call = Expression.NewCall(access, ArgumentList.New(Sequence.GetEmpty<Argument>()));
                return call;
            }
            if (n.IsNullable && IsNativeIntegerType(n.AsNullable.OperandType))
            {
                Expression access = Expression.NewNullableAccess(node, Identifier.New("ToInt64"));
                Expression call = Expression.NewCall(access, ArgumentList.New(Sequence.GetEmpty<Argument>()));
                return call;
            }
            return node;
        }

        private RelationalOp GetNumberArgumentComparisonOp(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (IsPositiveIntegerType(n))
                return RelationalOp.LessEqual;
            if (n.IsNullable && IsPositiveIntegerType(n.AsNullable.OperandType))
                return RelationalOp.LessEqual;
            return RelationalOp.LessThan;
        }

        private Sequence<Statement> GenerateArgumentNumberRangeCheck(Parameter n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var arg = Expression.NewNameReference(n.Name);
            var left = NumberArgumentToComparable(n.Type, arg);
            var op = GetNumberArgumentComparisonOp(n.Type);
            var zero = Expression.NewInteger(IntegerLiteral.New(0));
            var pair = RelationalOpValuePair.New(op, zero);
            var comp = Expression.NewRelational(left, Sequence.Create(pair));
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("ArgumentOutOfRangeException"));
            var nameOf = Expression.NewNameOf(arg);
            var str = TypeExpression.NewString();
            var strRef = Expression.NewBuiltinTypeReference(str);
            var strFormat = Expression.NewAccess(strRef, Identifier.New("Format"));
            Expression format;
            if (_grammar.UseAssemblyStringResources)
            {
                string id;
                if (op == RelationalOp.LessEqual)
                    id = "ArgumentMustBePositive";
                else
                    id = "ArgumentMustBeNonNegative";
                var srRef = Expression.NewTypeReference(_grammar.StringResourcesClassReference);
                format = Expression.NewAccess(srRef, Identifier.New(id));
            }
            else
            {
                string msg;
                if (op == RelationalOp.LessEqual)
                    msg = "\"Argument '{0}' must be positive.\"";
                else
                    msg = "\"Argument '{0}' must be non-negative.\"";
                format = Expression.NewString(StringLiteral.New(msg));
            }
            var callArg1 = Argument.NewPositional(format);
            var callArg2 = Argument.NewPositional(nameOf);
            var callArgs = ArgumentList.New(Sequence.Create(callArg1, callArg2));
            var call = Expression.NewCall(strFormat, callArgs);
            var excArg1 = Argument.NewPositional(nameOf);
            var excArg2 = Argument.NewPositional(call);
            var excArgs = ArgumentList.New(Sequence.Create(excArg1, excArg2));
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var elseIfs = Sequence.GetEmpty<ElseIfBranch>();
            var ifStmt = Statement.NewIf(comp, throwStmt, elseIfs);
            return Sequence.Create(ifStmt);
        }

        private Sequence<Statement> GenerateArgumentCheck(Parameter n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var check = Sequence.GetEmpty<Statement>();
            if (NeedArgumentNullCheck(n.Type))
                check = Sequence.Concat(check, GenerateArgumentNullCheck(n));
            if (NeedArgumentSequenceEmptyCheck(n.Type))
                check = Sequence.Concat(check, GenerateArgumentSequenceEmptyCheck(n));
            if (NeedArgumentNumberRangeCheck(n.Type))
                check = Sequence.Concat(check, GenerateArgumentNumberRangeCheck(n));
            if (!n.Type.IsNullable)
                return check;
            TypeExpression operandType = n.Type.AsNullable.OperandType;
            if (NeedArgumentSequenceEmptyCheck(operandType))
                check = Sequence.Concat(check, GenerateArgumentSequenceEmptyCheck(n));
            if (NeedArgumentNumberRangeCheck(operandType))
                check = Sequence.Concat(check, GenerateArgumentNumberRangeCheck(n));
            return check;
        }
        #endregion

        #region GenerateClassMembers
        private TypeExpression CreateTypeReference(string fullName)
        {
            if (fullName == null)
                throw new ArgumentNullException(nameof(fullName));
            Identifier id = Identifier.New(fullName);
            id.CheckDottedName();
            TypeExpression n = null;
            foreach (Identifier item in id.Items)
                if (n == null)
                    n = TypeExpression.NewGlobalNameReference(item);
                else
                    n = TypeExpression.NewAccess(n, item);
            return n;
        }

        private TypeExpression ResolveTypeReference(TypeExpression n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.IsNameReference && _derivedTypes.ContainsKey(n.AsNameReference.Name.Name))
                n = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + n.AsNameReference.Name.Name);
            return n;
        }

        private Identifier GetBackingFieldName(StructFieldDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            Identifier name = Identifier.New("_" + n.Name.Name);
            return name;
        }

        private Sequence<ClassMemberDeclarator> GenerateBackingFields(Sequence<StructFieldDeclarator> fields)
        {
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            foreach (StructFieldDeclarator f in fields)
            {
                if (f.IsMutable && NeedArgumentCheck(f.Type))
                {
                    var access = ClassMemberAccessibility.Private;
                    var fieldName = GetBackingFieldName(f);
                    var fieldType = ResolveTypeReference(f.Type);
                    var decl = ClassMemberDeclarator.NewField(access, false, fieldName, true, fieldType);
                    members = Sequence.Append(members, decl);
                }
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateProperties(Sequence<StructFieldDeclarator> fields)
        {
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            foreach (StructFieldDeclarator f in fields)
            {
                if (f.IsMutable && NeedArgumentCheck(f.Type))
                {
                    var fieldName = GetBackingFieldName(f);
                    var fieldRef = Expression.NewNameReference(fieldName);
                    var getter = ClassMethodBody.NewExpr(fieldRef);
                    var fieldType = ResolveTypeReference(f.Type);
                    var value = Parameter.New(Identifier.New("value"), fieldType);
                    var check = GenerateArgumentCheck(value);
                    var lhs = Sequence.Create(fieldRef);
                    var rhs = Expression.NewNameReference(value.Name);
                    var assign = Statement.NewAssign(lhs, rhs);
                    var block = Statement.NewBlock(Sequence.Append(check, assign));
                    var suite = Statement.NewSuite(true, block);
                    var setter = ClassMethodBody.NewBlock(suite);
                    var access = ClassMemberAccessibility.Public;
                    var name = f.Name.ToUpperCase();
                    var decl = ClassMemberDeclarator.NewPropertyBlock(access, false, name, fieldType, getter, setter);
                    members = Sequence.Append(members, decl);
                }
                else
                {
                    var access = ClassMemberAccessibility.Public;
                    var name = f.Name.ToUpperCase();
                    var fieldType = ResolveTypeReference(f.Type);
                    var decl = ClassMemberDeclarator.NewPropertyField(access, false, name, f.IsMutable, fieldType);
                    members = Sequence.Append(members, decl);
                }
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateIntrinsicProperties(UnionDeclarator n, Sequence<Identifier> tags)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (tags == null)
                throw new ArgumentNullException(nameof(tags));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var enumName = Identifier.New(n.Name.Name + "Category");
            var enumTypeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + enumName.Name);
            var access = ClassMemberAccessibility.Public;
            var tagPropertyName = Identifier.New("Tag");
            var tagProperty = ClassMemberDeclarator.NewAbstractProperty(access, tagPropertyName, false, enumTypeRef);
            members = Sequence.Append(members, tagProperty);
            foreach (Identifier tag in tags)
            {
                var name = Identifier.New("Is" + tag.Name);
                var type = TypeExpression.NewBool();
                var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + tag.Name + n.Name.Name);
                var value = Expression.NewIsInstanceOf(Expression.NewThisReference(), typeRef);
                var isProperty = ClassMemberDeclarator.NewProperty(access, false, name, type, value);
                members = Sequence.Append(members, isProperty);
            }
            foreach (Identifier tag in tags)
            {
                var name = Identifier.New("As" + tag.Name);
                var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + tag.Name + n.Name.Name);
                var value = Expression.NewAsInstanceOf(Expression.NewThisReference(), typeRef);
                var asProperty = ClassMemberDeclarator.NewProperty(access, false, name, typeRef, value);
                members = Sequence.Append(members, asProperty);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateIntrinsicProperties(UnionDeclarator n, Identifier tag)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (tag == null)
                throw new ArgumentNullException(nameof(tag));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var enumName = Identifier.New(n.Name.Name + "Category");
            var enumTypeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + enumName.Name);
            var access = ClassMemberAccessibility.Public;
            var modifier = ClassMemberVirtualModifier.Sealed;
            var name = Identifier.New("Tag");
            var value = Expression.NewAccess(Expression.NewTypeReference(enumTypeRef), tag);
            var decl = ClassMemberDeclarator.NewVirtualProperty(access, modifier, name, enumTypeRef, value);
            members = Sequence.Append(members, decl);
            return members;
        }

        private ClassMemberDeclarator GenerateConstructor(bool isPrivate, Sequence<StructFieldDeclarator> fields, Sequence<StructFieldDeclarator> prefixFields, Sequence<StructFieldDeclarator> postfixFields)
        {
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            if (prefixFields == null)
                throw new ArgumentNullException(nameof(prefixFields));
            if (postfixFields == null)
                throw new ArgumentNullException(nameof(postfixFields));
            var access = isPrivate ? ClassMemberAccessibility.Private : ClassMemberAccessibility.Internal;
            var methodParameters = Sequence.GetEmpty<Parameter>();
            var withDefaultNull = true;
            var allFields = Sequence.Concat(prefixFields, Sequence.Concat(fields, postfixFields));
            for (int i = allFields.Count - 1; i >= 0; i--)
            {
                StructFieldDeclarator f = allFields[i];
                Expression value = null;
                if (!f.Type.IsNullable)
                    withDefaultNull = false;
                else if (withDefaultNull)
                    value = Expression.NewNull(NullLiteral.New());
                TypeExpression fieldType = ResolveTypeReference(f.Type);
                Parameter parameter = Parameter.New(f.Name, fieldType, value);
                methodParameters = Sequence.Prepend(parameter, methodParameters);
            }
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, methodParameters, null, keywordOnly);
            var statements = Sequence.GetEmpty<Statement>();
            foreach (StructFieldDeclarator f in fields)
            {
                if (NeedArgumentCheck(f.Type))
                {
                    TypeExpression fieldType = ResolveTypeReference(f.Type);
                    Parameter parameter = Parameter.New(f.Name, fieldType);
                    statements = Sequence.Concat(statements, GenerateArgumentCheck(parameter));
                }
            }
            foreach (StructFieldDeclarator f in fields)
            {
                var lhsName = f.Name.ToUpperCase();
                var rhsName = f.Name;
                var lhsExpr = Expression.NewNameReference(lhsName);
                var rhsExpr = Expression.NewNameReference(rhsName);
                var assign = Statement.NewAssign(Sequence.Create(lhsExpr), rhsExpr);
                statements = Sequence.Append(statements, assign);
            }
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var baseFields = Sequence.Concat(prefixFields, postfixFields);
            if (baseFields.Count == 0)
            {
                var decl = ClassMemberDeclarator.NewConstructor(access, parameters, body);
                return decl;
            }
            else
            {
                var args = Sequence.GetEmpty<Argument>();
                foreach (StructFieldDeclarator f in baseFields)
                {
                    var arg = Argument.NewPositional(Expression.NewNameReference(f.Name));
                    args = Sequence.Append(args, arg);
                }
                var arguments = ArgumentList.New(args);
                var decl = ClassMemberDeclarator.NewConstructorWithBase(access, parameters, arguments, body);
                return decl;
            }
        }

        private ClassMemberDeclarator GenerateConstructor(bool isPrivate, Sequence<StructFieldDeclarator> fields)
        {
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            var prefixFields = Sequence.GetEmpty<StructFieldDeclarator>();
            var postfixFields = Sequence.GetEmpty<StructFieldDeclarator>();
            return GenerateConstructor(isPrivate, fields, prefixFields, postfixFields);
        }

        private ClassMemberDeclarator GenerateNewMethod(Identifier methodName, TypeExpression n, Sequence<StructFieldDeclarator> fields, TypeExpression resultType, bool isHiding)
        {
            if (methodName == null)
                throw new ArgumentNullException(nameof(methodName));
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            if (resultType == null)
                throw new ArgumentNullException(nameof(resultType));
            var methodParameters = Sequence.GetEmpty<Parameter>();
            var withDefaultNull = true;
            for (int i = fields.Count - 1; i >= 0; i--)
            {
                StructFieldDeclarator f = fields[i];
                Expression value = null;
                if (!f.Type.IsNullable)
                    withDefaultNull = false;
                else if (withDefaultNull)
                    value = Expression.NewNull(NullLiteral.New());
                TypeExpression fieldType = ResolveTypeReference(f.Type);
                Parameter parameter = Parameter.New(f.Name, fieldType, value);
                methodParameters = Sequence.Prepend(parameter, methodParameters);
            }
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, methodParameters, null, keywordOnly);
            var statements = Sequence.GetEmpty<Statement>();
            foreach (Parameter parameter in parameters.Positional)
                if (NeedArgumentCheck(parameter.Type))
                    statements = Sequence.Concat(statements, GenerateArgumentCheck(parameter));
            var newArgs = Sequence.GetEmpty<Argument>();
            foreach (StructFieldDeclarator f in fields)
            {
                var nameArg = Argument.NewPositional(Expression.NewNameReference(f.Name));
                newArgs = Sequence.Append(newArgs, nameArg);
            }
            var newExpr = Expression.NewNew(n, ArgumentList.New(newArgs));
            var returnStmt = Statement.NewReturn(newExpr);
            statements = Sequence.Append(statements, returnStmt);
            var access = isHiding ? ClassMemberAccessibility.NewPublic : ClassMemberAccessibility.Public;
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var decl = ClassMemberDeclarator.NewMethod(access, false, true, methodName, parameters, resultType, body);
            return decl;
        }

        private ClassMemberDeclarator GenerateCopyMethod(Identifier methodName, TypeExpression n, Sequence<StructFieldDeclarator> fields, TypeExpression resultType)
        {
            if (methodName == null)
                throw new ArgumentNullException(nameof(methodName));
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            if (resultType == null)
                throw new ArgumentNullException(nameof(resultType));
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var positional = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var newArgs = Sequence.GetEmpty<Argument>();
            foreach (StructFieldDeclarator f in fields)
            {
                var name = f.Name.ToUpperCase();
                var nameArg = Argument.NewPositional(Expression.NewNameReference(name));
                newArgs = Sequence.Append(newArgs, nameArg);
            }
            var newExpr = Expression.NewNew(n, ArgumentList.New(newArgs));
            var returnStmt = Statement.NewReturn(newExpr);
            var statements = Sequence.Create(returnStmt);
            var access = ClassMemberAccessibility.Public;
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var decl = ClassMemberDeclarator.NewMethod(access, false, false, methodName, parameters, resultType, body);
            return decl;
        }

        private Sequence<ClassMemberDeclarator> GenerateWithMethods(Identifier methodNamePrefix, TypeExpression n, Sequence<StructFieldDeclarator> fields, TypeExpression resultType)
        {
            if (methodNamePrefix == null)
                throw new ArgumentNullException(nameof(methodNamePrefix));
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            if (resultType == null)
                throw new ArgumentNullException(nameof(resultType));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            foreach (StructFieldDeclarator f in fields)
            {
                var methodName = Identifier.New(methodNamePrefix.Name + f.Name.ToUpperCase().Name);
                var fieldType = ResolveTypeReference(f.Type);
                var parameter = Parameter.New(f.Name, fieldType);
                var positionalOnly = Sequence.GetEmpty<Parameter>();
                var positional = Sequence.Create(parameter);
                var keywordOnly = Sequence.GetEmpty<Parameter>();
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var statements = Sequence.GetEmpty<Statement>();
                if (NeedArgumentCheck(f.Type))
                    statements = Sequence.Concat(statements, GenerateArgumentCheck(parameter));
                var newArgs = Sequence.GetEmpty<Argument>();
                foreach (StructFieldDeclarator field in fields)
                {
                    var name = (field == f) ? f.Name : field.Name.ToUpperCase();
                    var nameArg = Argument.NewPositional(Expression.NewNameReference(name));
                    newArgs = Sequence.Append(newArgs, nameArg);
                }
                var newExpr = Expression.NewNew(n, ArgumentList.New(newArgs));
                var returnStmt = Statement.NewReturn(newExpr);
                statements = Sequence.Append(statements, returnStmt);
                var access = ClassMemberAccessibility.Public;
                var block = Statement.NewBlock(statements);
                var suite = Statement.NewSuite(true, block);
                var body = ClassMethodBody.NewBlock(suite);
                var decl = ClassMemberDeclarator.NewMethod(access, false, false, methodName, parameters, resultType, body);
                members = Sequence.Append(members, decl);
            }
            return members;
        }

        //
        // TODO: xhd: implement ToSyntax
        //

        private Sequence<ClassMemberDeclarator> GenerateToSyntaxMethod(Identifier tagName, Sequence<StructFieldDeclarator> fields, bool isDerivedType)
        {
            if (tagName == null)
                throw new ArgumentNullException(nameof(tagName));
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var access = ClassMemberAccessibility.Public;
            var name = Identifier.New("ToSyntax");
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var positional = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var resultType = TypeExpression.NewString();
            var toSyntaxBody = Sequence.GetEmpty<Statement>();

            var sbName = Identifier.New("sb");
            var sbType = CreateTypeReference("System.Text.StringBuilder");
            var sbNew = Expression.NewNew(sbType, ArgumentList.New(Sequence.GetEmpty<Argument>()));
            var sbBind = Statement.NewLet(sbName, sbType, sbNew);
            toSyntaxBody = Sequence.Append(toSyntaxBody, sbBind);

            var sbAppMethodRef = Expression.NewAccess(Expression.NewNameReference(sbName), Identifier.New("Append"));
            var sbAppTagArgTag = Argument.NewPositional(Expression.NewString(StringLiteral.New(tagName.Name)));
            var sbAppTagArgs = ArgumentList.New(Sequence.Create(sbAppTagArgTag));
            var sbAppTagCall = Expression.NewCall(sbAppMethodRef, sbAppTagArgs);
            var sbAppTagCallStmt = Statement.NewExpr(sbAppTagCall);
            toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppTagCallStmt);

            var sbAppLeftParenArgLeftParen = Argument.NewPositional(Expression.NewString(StringLiteral.New("(")));
            var sbAppLeftParenArgs = ArgumentList.New(Sequence.Create(sbAppLeftParenArgLeftParen));
            var sbAppLeftParenCall = Expression.NewCall(sbAppMethodRef, sbAppLeftParenArgs);
            var sbAppLeftParenCallStmt = Statement.NewExpr(sbAppLeftParenCall);
            toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppLeftParenCallStmt);

            for (int i = 0; i < fields.Count; i++)
            {
                StructFieldDeclarator f = fields[i];

                if (i > 0)
                {
                    var sbAppCommaArgColon = Argument.NewPositional(Expression.NewString(StringLiteral.New(", ")));
                    var sbAppCommaArgs = ArgumentList.New(Sequence.Create(sbAppCommaArgColon));
                    var sbAppCommaCall = Expression.NewCall(sbAppMethodRef, sbAppCommaArgs);
                    var sbAppCommaCallStmt = Statement.NewExpr(sbAppCommaCall);
                    toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppCommaCallStmt);
                }

                var sbAppFieldNameArgTag = Argument.NewPositional(Expression.NewString(StringLiteral.New(f.Name.Name)));
                var sbAppFieldNameArgs = ArgumentList.New(Sequence.Create(sbAppFieldNameArgTag));
                var sbAppFieldCall = Expression.NewCall(sbAppMethodRef, sbAppFieldNameArgs);
                var sbAppFieldCallStmt = Statement.NewExpr(sbAppFieldCall);
                toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppFieldCallStmt);

                var sbAppColoonArgColon = Argument.NewPositional(Expression.NewString(StringLiteral.New(":")));
                var sbAppColonArgs = ArgumentList.New(Sequence.Create(sbAppColoonArgColon));
                var sbAppColonCall = Expression.NewCall(sbAppMethodRef, sbAppColonArgs);
                var sbAppColonCallStmt = Statement.NewExpr(sbAppColonCall);
                toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppColonCallStmt);
            }

            var sbAppRightParenArgRightParen = Argument.NewPositional(Expression.NewString(StringLiteral.New(")")));
            var sbAppRightParenArgs = ArgumentList.New(Sequence.Create(sbAppRightParenArgRightParen));
            var sbAppRightParenCall = Expression.NewCall(sbAppMethodRef, sbAppRightParenArgs);
            var sbAppRightParenCallStmt = Statement.NewExpr(sbAppRightParenCall);
            toSyntaxBody = Sequence.Append(toSyntaxBody, sbAppRightParenCallStmt);

            var sbToStringMethodRef = Expression.NewAccess(Expression.NewNameReference(sbName), Identifier.New("ToString"));
            var sbToStringArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var sbToStringCall = Expression.NewCall(sbToStringMethodRef, sbToStringArgs);
            var returnStmt = Statement.NewReturn(sbToStringCall);
            toSyntaxBody = Sequence.Append(toSyntaxBody, returnStmt);

            var block = Statement.NewBlock(toSyntaxBody);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            ClassMemberDeclarator method;
            if (isDerivedType)
                method = ClassMemberDeclarator.NewVirtualMethod(access, false, ClassMemberVirtualModifier.Sealed, name, parameters, resultType, body);
            else
                method = ClassMemberDeclarator.NewMethod(access, false, false, name, parameters, resultType, body);
            members = Sequence.Append(members, method);
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateToStringMethod(Identifier n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var access = ClassMemberAccessibility.Public;
            var name = Identifier.New("ToString");
            var writerInterfaceRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterInterfaceName.Name);
            var vName = Identifier.New("v");
            var vParam = Parameter.New(vName, writerInterfaceRef);
            var dName = Identifier.New("d");
            var dParam = Parameter.New(dName, TypeExpression.NewInt());
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var positional = Sequence.Create(vParam);
            if (n.Name.EndsWith("Expression"))
                positional = Sequence.Append(positional, dParam);
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var resultType = TypeExpression.NewString();
            var writerName = Identifier.New("writer");
            var writerType = CreateTypeReference("System.IO.StringWriter");
            var writerNew = Expression.NewNew(writerType, ArgumentList.New(Sequence.GetEmpty<Argument>()));
            var writerBind = WithBinding.New(writerNew, Expression.NewNameReference(writerName));
            var writerBody = Sequence.GetEmpty<Statement>();
            var wName = Identifier.New("w");
            var wType = _grammar.IndentedWriterClassReference;
            var wArg = Argument.NewPositional(Expression.NewNameReference(writerName));
            var wNew = Expression.NewNew(wType, ArgumentList.New(Sequence.Create(wArg)));
            var wBind = Statement.NewLet(wName, wType, wNew);
            writerBody = Sequence.Append(writerBody, wBind);
            var writeMethodName = Identifier.New("Write" + n.Name);
            var writeMethodRef = Expression.NewAccess(Expression.NewNameReference(vName), writeMethodName);
            var writeMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
            var writeMethodArgW = Argument.NewPositional(Expression.NewNameReference(wName));
            var writerMethodArgD = Argument.NewPositional(Expression.NewNameReference(dName));
            var writeMethodArgs = Sequence.Create(writeMethodArgThis, writeMethodArgW);
            if (n.Name.EndsWith("Expression"))
                writeMethodArgs = Sequence.Append(writeMethodArgs, writerMethodArgD);
            var writeMethodCall = Expression.NewCall(writeMethodRef, ArgumentList.New(writeMethodArgs));
            var writeStmt = Statement.NewExpr(writeMethodCall);
            writerBody = Sequence.Append(writerBody, writeStmt);
            var toStringRef = Expression.NewAccess(Expression.NewNameReference(writerName), Identifier.New("ToString"));
            var toStringArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var toStringCall = Expression.NewCall(toStringRef, toStringArgs);
            var trimRef = Expression.NewAccess(toStringCall, Identifier.New("Trim"));
            var trimArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var trimCall = Expression.NewCall(trimRef, trimArgs);
            var returnStmt = Statement.NewReturn(trimCall);
            writerBody = Sequence.Append(writerBody, returnStmt);
            var writerBlock = Statement.NewBlock(writerBody);
            var writerSuite = Statement.NewSuite(true, writerBlock);
            var writerWith = Statement.NewWith(false, Sequence.Create(writerBind), writerSuite);
            var actions = GenerateArgumentNullCheck(vParam);
            actions = Sequence.Append(actions, writerWith);
            var block = Statement.NewBlock(actions);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var method = ClassMemberDeclarator.NewMethod(access, false, false, name, parameters, resultType, body);
            members = Sequence.Append(members, method);
            if (n.Name.EndsWith("Expression"))
            {
                positional = Sequence.Create(vParam);
                parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var toStringBody = GenerateArgumentNullCheck(vParam);
                var toStringMethodRef = Expression.NewNameReference(name);
                var toStringMethodArgV = Argument.NewPositional(Expression.NewNameReference(vName));
                var toStringMethodArgD = Argument.NewPositional(Expression.NewInteger(IntegerLiteral.New(-1)));
                var toStringMethodArgs = ArgumentList.New(Sequence.Create(toStringMethodArgV, toStringMethodArgD));
                var toStringMethodCall = Expression.NewCall(toStringMethodRef, toStringMethodArgs);
                returnStmt = Statement.NewReturn(toStringMethodCall);
                toStringBody = Sequence.Append(toStringBody, returnStmt);
                block = Statement.NewBlock(toStringBody);
                suite = Statement.NewSuite(true, block);
                body = ClassMethodBody.NewBlock(suite);
                method = ClassMemberDeclarator.NewMethod(access, false, false, name, parameters, resultType, body);
                members = Sequence.Append(members, method);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateDefaultToStringMethod(bool isValueType)
        {
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var access = ClassMemberAccessibility.Public;
            var modifier = isValueType ? ClassMemberVirtualModifier.Override : ClassMemberVirtualModifier.Sealed;
            var name = Identifier.New("ToString");
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var positional = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var resultType = TypeExpression.NewString();
            var toStringBody = Sequence.GetEmpty<Statement>();
            var writerClassRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterClassName.Name);
            var instance = Expression.NewAccess(Expression.NewTypeReference(writerClassRef), Identifier.New("Instance"));
            var toStringMethodRef = Expression.NewNameReference(name);
            var toStringMethodArgV = Argument.NewPositional(instance);
            var toStringMethodArgs = ArgumentList.New(Sequence.Create(toStringMethodArgV));
            var toStringMethodCall = Expression.NewCall(toStringMethodRef, toStringMethodArgs);
            var returnStmt = Statement.NewReturn(toStringMethodCall);
            toStringBody = Sequence.Append(toStringBody, returnStmt);
            var block = Statement.NewBlock(toStringBody);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, name, parameters, resultType, body);
            members = Sequence.Append(members, method);
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptVisitorMethods(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            ReadOnlyCollection<VisitorDeclarator> visitors;
            if (!_referenceVisitors.TryGetValue(n.Name.Name, out visitors))
                return members;
            foreach (VisitorDeclarator visitor in visitors)
            {
                string visitorName = visitor.Name.Name;
                var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + visitorName);
                var vName = Identifier.New("v");
                var parameters = CreateNewVariant(visitor.Parameters, vName, vType);
                var acceptAccess = ClassMemberAccessibility.Public;
                if (visitorName.StartsWith("I"))
                    visitorName = visitorName.Substring(1);
                var acceptName = Identifier.New("Accept" + visitorName);
                var acceptMethod = ClassMemberDeclarator.NewAbstractMethod(acceptAccess, acceptName, parameters, visitor.ResultType);
                members = Sequence.Append(members, acceptMethod);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptVisitorMethods(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            ReadOnlyCollection<VisitorDeclarator> visitors;
            if (!_referenceVisitors.TryGetValue(n.Name.Name, out visitors))
                return members;
            foreach (VisitorDeclarator visitor in visitors)
            {
                string visitorName = visitor.Name.Name;
                var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + visitorName);
                var vName = Identifier.New("v");
                var parameters = CreateNewVariant(visitor.Parameters, vName, vType);
                var acceptAccess = ClassMemberAccessibility.Public;
                var modifier = ClassMemberVirtualModifier.Sealed;
                if (visitorName.StartsWith("I"))
                    visitorName = visitorName.Substring(1);
                var acceptName = Identifier.New("Accept" + visitorName);
                var visitMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
                var visitMethodName = Identifier.New(visitor.MethodNamePrefix.Name + c.Name.Name + n.Name.Name);
                var visitMethodRef = Expression.NewAccess(Expression.NewNameReference(vName), visitMethodName);
                var actions = Sequence.GetEmpty<Statement>();
                var visitArgs = Sequence.Create(visitMethodArgThis);
                foreach (Parameter param in parameters.Positional)
                {
                    actions = Sequence.Concat(actions, GenerateArgumentCheck(param));
                    if (param.Name != vName)
                    {
                        var paramRef = Expression.NewNameReference(param.Name);
                        var visitArg = Argument.NewPositional(paramRef);
                        visitArgs = Sequence.Append(visitArgs, visitArg);
                    }
                }
                var visitMethodArgs = ArgumentList.New(visitArgs);
                var visitMethodCall = Expression.NewCall(visitMethodRef, visitMethodArgs);
                if (visitor.ResultType == null)
                    actions = Sequence.Append(actions, Statement.NewExpr(visitMethodCall));
                else
                    actions = Sequence.Append(actions, Statement.NewReturn(visitMethodCall));
                var block = Statement.NewBlock(actions);
                var suite = Statement.NewSuite(true, block);
                var body = ClassMethodBody.NewBlock(suite);
                var acceptMethod = ClassMemberDeclarator.NewVirtualMethod(acceptAccess, false, modifier, acceptName, parameters, visitor.ResultType, body);
                members = Sequence.Append(members, acceptMethod);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptWriterMethod(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterInterfaceName.Name);
            var vName = Identifier.New("v");
            var vParam = Parameter.New(vName, vType);
            var wType = _grammar.IndentedWriterClassReference;
            var wName = Identifier.New("writer");
            var wParam = Parameter.New(wName, wType);
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            if (!n.Name.Name.EndsWith("Expression"))
            {
                var positional = Sequence.Create(vParam, wParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var acceptAccess = ClassMemberAccessibility.Public;
                var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWriterClassName.Name);
                var acceptMethod = ClassMemberDeclarator.NewAbstractMethod(acceptAccess, acceptName, parameters);
                members = Sequence.Append(members, acceptMethod);
            }
            else
            {
                var dType = TypeExpression.NewInt();
                var dName = Identifier.New("d");
                var dParam = Parameter.New(dName, dType);
                var positional = Sequence.Create(vParam, wParam, dParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var acceptAccess = ClassMemberAccessibility.Public;
                var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWriterClassName.Name);
                var acceptMethod = ClassMemberDeclarator.NewAbstractMethod(acceptAccess, acceptName, parameters);
                members = Sequence.Append(members, acceptMethod);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptWriterMethod(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterInterfaceName.Name);
            var vName = Identifier.New("v");
            var vParam = Parameter.New(vName, vType);
            var wType = _grammar.IndentedWriterClassReference;
            var wName = Identifier.New("writer");
            var wParam = Parameter.New(wName, wType);
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var writeMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
            var writeMethodArgW = Argument.NewPositional(Expression.NewNameReference(wParam.Name));
            var writeMethodName = Identifier.New("Write" + c.Name.Name + n.Name.Name);
            var writeMethodRef = Expression.NewAccess(Expression.NewNameReference(vName), writeMethodName);
            var actions = Sequence.GetEmpty<Statement>();
            actions = Sequence.Concat(actions, GenerateArgumentNullCheck(vParam));
            actions = Sequence.Concat(actions, GenerateArgumentNullCheck(wParam));
            if (!n.Name.Name.EndsWith("Expression"))
            {
                var writeMethodArgs = ArgumentList.New(Sequence.Create(writeMethodArgThis, writeMethodArgW));
                var writeMethodCall = Expression.NewCall(writeMethodRef, writeMethodArgs);
                actions = Sequence.Append(actions, Statement.NewExpr(writeMethodCall));
                var block = Statement.NewBlock(actions);
                var suite = Statement.NewSuite(true, block);
                var body = ClassMethodBody.NewBlock(suite);
                var positional = Sequence.Create(vParam, wParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var acceptAccess = ClassMemberAccessibility.Public;
                var modifier = ClassMemberVirtualModifier.Sealed;
                var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWriterClassName.Name);
                var acceptMethod = ClassMemberDeclarator.NewVirtualMethod(acceptAccess, false, modifier, acceptName, parameters, null, body);
                members = Sequence.Append(members, acceptMethod);
            }
            else
            {
                var dType = TypeExpression.NewInt();
                var dName = Identifier.New("d");
                var dParam = Parameter.New(dName, dType);
                var writeMethodArgD = Argument.NewPositional(Expression.NewNameReference(dName));
                var writeMethodArgs = ArgumentList.New(Sequence.Create(writeMethodArgThis, writeMethodArgW, writeMethodArgD));
                var writeMethodCall = Expression.NewCall(writeMethodRef, writeMethodArgs);
                actions = Sequence.Append(actions, Statement.NewExpr(writeMethodCall));
                var block = Statement.NewBlock(actions);
                var suite = Statement.NewSuite(true, block);
                var body = ClassMethodBody.NewBlock(suite);
                var positional = Sequence.Create(vParam, wParam, dParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var acceptAccess = ClassMemberAccessibility.Public;
                var modifier = ClassMemberVirtualModifier.Sealed;
                var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWriterClassName.Name);
                var acceptMethod = ClassMemberDeclarator.NewVirtualMethod(acceptAccess, false, modifier, acceptName, parameters, null, body);
                members = Sequence.Append(members, acceptMethod);
            }
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptWalkerMethod(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWalkerClassName.Name);
            var vName = Identifier.New("v");
            var vParam = Parameter.New(vName, vType);
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var positional = Sequence.Create(vParam);
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var acceptAccess = ClassMemberAccessibility.Public;
            var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWalkerClassName.Name);
            var acceptMethod = ClassMemberDeclarator.NewAbstractMethod(acceptAccess, acceptName, parameters);
            members = Sequence.Append(members, acceptMethod);
            return members;
        }

        private Sequence<ClassMemberDeclarator> GenerateAcceptWalkerMethod(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            if (!_grammar.UseSyntaxTreeNodeTypes)
                return members;
            var vType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWalkerClassName.Name);
            var vName = Identifier.New("v");
            var vParam = Parameter.New(vName, vType);
            var positionalOnly = Sequence.GetEmpty<Parameter>();
            var keywordOnly = Sequence.GetEmpty<Parameter>();
            var walkMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
            var walkMethodName = Identifier.New("Walk" + c.Name.Name + n.Name.Name);
            var walkMethodRef = Expression.NewAccess(Expression.NewNameReference(vName), walkMethodName);
            var actions = Sequence.GetEmpty<Statement>();
            actions = Sequence.Concat(actions, GenerateArgumentNullCheck(vParam));
            var walkMethodArgs = ArgumentList.New(Sequence.Create(walkMethodArgThis));
            var walkMethodCall = Expression.NewCall(walkMethodRef, walkMethodArgs);
            actions = Sequence.Append(actions, Statement.NewExpr(walkMethodCall));
            var block = Statement.NewBlock(actions);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            var positional = Sequence.Create(vParam);
            var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
            var acceptAccess = ClassMemberAccessibility.Public;
            var modifier = ClassMemberVirtualModifier.Sealed;
            var acceptName = Identifier.New("Accept" + _syntaxTreeNodeWalkerClassName.Name);
            var acceptMethod = ClassMemberDeclarator.NewVirtualMethod(acceptAccess, false, modifier, acceptName, parameters, null, body);
            members = Sequence.Append(members, acceptMethod);
            return members;
        }

        private Expression GetNodeChildCountLiteral(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            Sequence<StructFieldDeclarator> fields;
            if (!_childNodes.TryGetValue(name.Name, out fields))
                return Expression.NewInteger(IntegerLiteral.New(0));
            if (fields.Count <= 0)
                return Expression.NewInteger(IntegerLiteral.New(0));
            foreach (StructFieldDeclarator field in fields)
                if (!field.Type.IsNameReference)
                    return null;
            return Expression.NewInteger(IntegerLiteral.New(fields.Count));
        }

        private Expression GetChildCountExpression(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            var fields = _childNodes[name.Name];
            foreach (StructFieldDeclarator field in fields)
                if (field.Type.IsNullable)
                    return null;
            int num = 0;
            foreach (StructFieldDeclarator field in fields)
                if (field.Type.IsNameReference)
                    num++;
            Expression e = null;
            foreach (StructFieldDeclarator field in fields)
            {
                if (field.Type.IsNameReference)
                {
                }
                else if (field.Type.IsNullable)
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var testOp = RelationalOpValuePair.New(RelationalOp.IsNot, Expression.NewNull(NullLiteral.New()));
                    var testOps = Sequence.Create(testOp);
                    var test = Expression.NewRelational(fieldRef, testOps);
                    var c = Expression.NewConditional(test, Expression.NewInteger(IntegerLiteral.New(1)), Expression.NewInteger(IntegerLiteral.New(0)));
                    if (e != null)
                        e = Expression.NewAdd(e, c);
                    else if (num == 0)
                        e = c;
                    else
                        e = Expression.NewAdd(Expression.NewInteger(IntegerLiteral.New(num)), c);
                }
                else
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var c = Expression.NewAccess(fieldRef, Identifier.New("Count"));
                    if (e != null)
                        e = Expression.NewAdd(e, c);
                    else if (num == 0)
                        e = c;
                    else
                        e = Expression.NewAdd(Expression.NewInteger(IntegerLiteral.New(num)), c);
                }
            }
            return e;
        }

        private ClassMethodBody GetChildCountBody(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            var fields = _childNodes[name.Name];
            var statements = Sequence.GetEmpty<Statement>();
            var countName = Identifier.New("count");
            var countRef = Expression.NewNameReference(countName);
            var countDecl = Statement.NewVar(countName, TypeExpression.NewInt(), Expression.NewInteger(IntegerLiteral.New(0)));
            statements = Sequence.Append(statements, countDecl);
            foreach (StructFieldDeclarator field in fields)
            {
                if (field.Type.IsNameReference)
                {
                    var stmt = Statement.NewExpr(Expression.NewPostfixIncrement(countRef));
                    statements = Sequence.Append(statements, stmt);
                }
                else if (field.Type.IsNullable)
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var testOp = RelationalOpValuePair.New(RelationalOp.IsNot, Expression.NewNull(NullLiteral.New()));
                    var testOps = Sequence.Create(testOp);
                    var test = Expression.NewRelational(fieldRef, testOps);
                    var c = Expression.NewConditional(test, Expression.NewInteger(IntegerLiteral.New(1)), Expression.NewInteger(IntegerLiteral.New(0)));
                    var stmt = Statement.NewAddAssign(countRef, c);
                    statements = Sequence.Append(statements, stmt);
                }
                else
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var c = Expression.NewAccess(fieldRef, Identifier.New("Count"));
                    var stmt = Statement.NewAddAssign(countRef, c);
                    statements = Sequence.Append(statements, stmt);
                }
            }
            var returnStmt = Statement.NewReturn(countRef);
            statements = Sequence.Append(statements, returnStmt);
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            return body;
        }

        private ClassMemberDeclarator GenerateChildCountProperty(StructDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyCollection");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var name = Identifier.New("Count");
            var type = TypeExpression.NewNonNegativeInt();
            var num = GetNodeChildCountLiteral(n.Name);
            if (num == null)
                num = GetChildCountExpression(n.Name);
            if (num != null)
                return ClassMemberDeclarator.NewExplicitPropertyImpl(target, name, type, num);
            var body = GetChildCountBody(n.Name);
            return ClassMemberDeclarator.NewExplicitPropertyImplBlock(target, name, type, body);
        }

        private ClassMemberDeclarator GenerateChildCountProperty(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyCollection");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var name = Identifier.New("Count");
            var type = TypeExpression.NewNonNegativeInt();
            var typeName = Identifier.New(c.Name.Name + n.Name.Name);
            var num = GetNodeChildCountLiteral(typeName);
            if (num == null)
                num = GetChildCountExpression(typeName);
            if (num != null)
                return ClassMemberDeclarator.NewExplicitPropertyImpl(target, name, type, num);
            var body = GetChildCountBody(typeName);
            return ClassMemberDeclarator.NewExplicitPropertyImplBlock(target, name, type, body);
        }

        private ClassMemberDeclarator GenerateChildCountProperty(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyCollection");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var name = Identifier.New("Count");
            var type = TypeExpression.NewNonNegativeInt();
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("NotImplementedException"));
            var excArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var statements = Sequence.Create(throwStmt);
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            return ClassMemberDeclarator.NewExplicitPropertyImplBlock(target, name, type, body);
        }

        private ClassMethodBody GenerateGetChildIndexerSwitch(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            var clauses = Sequence.GetEmpty<SwitchCaseClause>();
            Sequence<StructFieldDeclarator> fields;
            if (_childNodes.TryGetValue(name.Name, out fields))
            {
                int index = 0;
                foreach (StructFieldDeclarator field in fields)
                {
                    if (!field.Type.IsNameReference)
                        return null;
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var caseValue = Expression.NewInteger(IntegerLiteral.New(index++));
                    var caseReturn = Statement.NewReturn(fieldRef);
                    var clause = SwitchCaseClause.New(caseValue, caseReturn);
                    clauses = Sequence.Append(clauses, clause);
                }
            }
            var paramName = Identifier.New("i");
            var paramType = TypeExpression.NewNonNegativeInt();
            var param = Parameter.New(paramName, paramType);
            var defaultActions = Sequence.GetEmpty<Statement>();
            defaultActions = Sequence.Concat(defaultActions, GenerateArgumentCheck(param));
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("ArgumentOutOfRangeException"));
            var arg = Expression.NewNameReference(paramName);
            var nameOf = Expression.NewNameOf(arg);
            var excArg = Argument.NewPositional(nameOf);
            var excArgs = ArgumentList.New(Sequence.Create(excArg));
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            defaultActions = Sequence.Append(defaultActions, throwStmt);
            var defaultBlock = Statement.NewBlock(defaultActions);
            var defaultSuite = Statement.NewSuite(true, defaultBlock);
            var switchStmt = Statement.NewSwitch(arg, clauses, defaultSuite);
            var block = Statement.NewBlock(Sequence.Create(switchStmt));
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            return body;
        }

        private ClassMethodBody GenerateGetChildIndexerBody(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            var body = GenerateGetChildIndexerSwitch(name);
            if (body != null)
                return body;
            var fields = _childNodes[name.Name];
            var paramName = Identifier.New("i");
            var paramType = TypeExpression.NewNonNegativeInt();
            var param = Parameter.New(paramName, paramType);
            var statements = Sequence.GetEmpty<Statement>();
            statements = Sequence.Concat(statements, GenerateArgumentCheck(param));
            var paramRef = Expression.NewNameReference(paramName);
            var countName = Identifier.New("count");
            var countRef = Expression.NewNameReference(countName);
            var countDecl = Statement.NewVar(countName, paramType, Expression.NewInteger(IntegerLiteral.New(0)));
            statements = Sequence.Append(statements, countDecl);
            foreach (StructFieldDeclarator field in fields)
            {
                if (field.Type.IsNameReference)
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var returnStmt = Statement.NewReturn(fieldRef);
                    var paramTestOp = RelationalOpValuePair.New(RelationalOp.EqualTo, countRef);
                    var paramTestOps = Sequence.Create(paramTestOp);
                    var paramTest = Expression.NewRelational(paramRef, paramTestOps);
                    var paramTestRet = Statement.NewIf(paramTest, returnStmt, Sequence.GetEmpty<ElseIfBranch>());
                    var incCountStmt = Statement.NewExpr(Expression.NewPostfixIncrement(countRef));
                    statements = Sequence.Append(statements, paramTestRet);
                    statements = Sequence.Append(statements, incCountStmt);
                }
                else if (field.Type.IsNullable)
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var returnStmt = Statement.NewReturn(fieldRef);
                    var paramTestOp = RelationalOpValuePair.New(RelationalOp.EqualTo, countRef);
                    var paramTestOps = Sequence.Create(paramTestOp);
                    var paramTest = Expression.NewRelational(paramRef, paramTestOps);
                    var paramTestRet = Statement.NewIf(paramTest, returnStmt, Sequence.GetEmpty<ElseIfBranch>());
                    var incCountStmt = Statement.NewExpr(Expression.NewPostfixIncrement(countRef));
                    var testOp = RelationalOpValuePair.New(RelationalOp.IsNot, Expression.NewNull(NullLiteral.New()));
                    var testOps = Sequence.Create(testOp);
                    var test = Expression.NewRelational(fieldRef, testOps);
                    var trueBlock = Statement.NewBlock(Sequence.Create(paramTestRet, incCountStmt));
                    var trueSuite = Statement.NewSuite(true, trueBlock);
                    var stmt = Statement.NewIf(test, trueSuite, Sequence.GetEmpty<ElseIfBranch>());
                    statements = Sequence.Append(statements, stmt);
                }
                else
                {
                    var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                    var fieldIndex = Subscript.NewIndex(Expression.NewSubtract(paramRef, countRef));
                    var fieldIndices = SubscriptList.New(Sequence.Create(fieldIndex), false);
                    var fieldSubscript = Expression.NewSubscript(fieldRef, fieldIndices);
                    var returnStmt = Statement.NewReturn(fieldSubscript);
                    var c = Expression.NewAccess(fieldRef, Identifier.New("Count"));
                    var paramTestOp = RelationalOpValuePair.New(RelationalOp.LessEqual, paramRef);
                    var paramTestOp2 = RelationalOpValuePair.New(RelationalOp.LessThan, Expression.NewAdd(countRef, c));
                    var paramTestOps = Sequence.Create(paramTestOp, paramTestOp2);
                    var paramTest = Expression.NewRelational(countRef, paramTestOps);
                    var paramTestRet = Statement.NewIf(paramTest, returnStmt, Sequence.GetEmpty<ElseIfBranch>());
                    var incCountStmt = Statement.NewAddAssign(countRef, c);
                    statements = Sequence.Append(statements, paramTestRet);
                    statements = Sequence.Append(statements, incCountStmt);
                }
            }
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("ArgumentOutOfRangeException"));
            var arg = Expression.NewNameReference(paramName);
            var nameOf = Expression.NewNameOf(arg);
            var excArg = Argument.NewPositional(nameOf);
            var excArgs = ArgumentList.New(Sequence.Create(excArg));
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            statements = Sequence.Append(statements, throwStmt);
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            return ClassMethodBody.NewBlock(suite);
        }

        private ClassMemberDeclarator GenerateGetChildIndexer(StructDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyList");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var paramName = Identifier.New("i");
            var paramType = TypeExpression.NewNonNegativeInt();
            var param = Parameter.New(paramName, paramType);
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.Create(param), null, Sequence.GetEmpty<Parameter>());
            var body = GenerateGetChildIndexerBody(n.Name);
            return ClassMemberDeclarator.NewExplicitIndexerImplBlock(target, parameters, _grammar.SyntaxTreeNodeInterfaceReference, body);
        }

        private ClassMemberDeclarator GenerateGetChildIndexer(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyList");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var paramName = Identifier.New("i");
            var paramType = TypeExpression.NewNonNegativeInt();
            var param = Parameter.New(paramName, paramType);
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.Create(param), null, Sequence.GetEmpty<Parameter>());
            var name = Identifier.New(c.Name.Name + n.Name.Name);
            var body = GenerateGetChildIndexerBody(name);
            return ClassMemberDeclarator.NewExplicitIndexerImplBlock(target, parameters, _grammar.SyntaxTreeNodeInterfaceReference, body);
        }

        private ClassMemberDeclarator GenerateGetChildIndexer(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IReadOnlyList");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var paramName = Identifier.New("i");
            var paramType = TypeExpression.NewNonNegativeInt();
            var param = Parameter.New(paramName, paramType);
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.Create(param), null, Sequence.GetEmpty<Parameter>());
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("NotImplementedException"));
            var excArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var statements = Sequence.Create(throwStmt);
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            return ClassMemberDeclarator.NewExplicitIndexerImplBlock(target, parameters, _grammar.SyntaxTreeNodeInterfaceReference, body);
        }

        private ClassMethodBody GenerateEnumChildMethodBody(Identifier name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            var statements = Sequence.GetEmpty<Statement>();
            Sequence<StructFieldDeclarator> fields;
            if (!_childNodes.TryGetValue(name.Name, out fields) || fields.Count <= 0)
                statements = Sequence.Append(statements, Statement.NewExpr(Expression.NewYieldBreak()));
            else
            {
                foreach (StructFieldDeclarator field in fields)
                {
                    if (field.Type.IsNameReference)
                    {
                        var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                        var yieldStmt = Statement.NewExpr(Expression.NewYield(fieldRef));
                        statements = Sequence.Append(statements, yieldStmt);
                    }
                    else if (field.Type.IsNullable)
                    {
                        var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                        var yieldStmt = Statement.NewExpr(Expression.NewYield(fieldRef));
                        var testOp = RelationalOpValuePair.New(RelationalOp.IsNot, Expression.NewNull(NullLiteral.New()));
                        var testOps = Sequence.Create(testOp);
                        var test = Expression.NewRelational(fieldRef, testOps);
                        var stmt = Statement.NewIf(test, yieldStmt, Sequence.GetEmpty<ElseIfBranch>());
                        statements = Sequence.Append(statements, stmt);
                    }
                    else if (fields.Count > 1)
                    {
                        var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                        var yieldStmt = Statement.NewExpr(Expression.NewYieldFrom(fieldRef));
                        statements = Sequence.Append(statements, yieldStmt);
                    }
                    else
                    {
                        var fieldRef = Expression.NewNameReference(field.Name.ToUpperCase());
                        var memberRef = Expression.NewAccess(fieldRef, Identifier.New("GetEnumerator"));
                        var getExpr = Expression.NewCall(memberRef, ArgumentList.New(Sequence.GetEmpty<Argument>()));
                        var returnStmt = Statement.NewReturn(getExpr);
                        statements = Sequence.Append(statements, returnStmt);
                    }
                }
            }
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            return ClassMethodBody.NewBlock(suite);
        }

        private ClassMemberDeclarator GenerateEnumChildMethod(StructDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IEnumerable");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var enumerator = CreateTypeReference("System.Collections.Generic.IEnumerator");
            var enumeratorArgs = Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference);
            var resultType = TypeExpression.NewBoundedGeneric(enumerator, enumeratorArgs);
            var name = Identifier.New("GetEnumerator");
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.GetEmpty<Parameter>(), null, Sequence.GetEmpty<Parameter>());
            var body = GenerateEnumChildMethodBody(n.Name);
            return ClassMemberDeclarator.NewExplicitMethodImpl(false, target, name, parameters, resultType, body);
        }

        private ClassMemberDeclarator GenerateEnumChildMethod(UnionDeclarator n, UnionCaseDeclarator c)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            var coll = CreateTypeReference("System.Collections.Generic.IEnumerable");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var enumerator = CreateTypeReference("System.Collections.Generic.IEnumerator");
            var enumeratorArgs = Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference);
            var resultType = TypeExpression.NewBoundedGeneric(enumerator, enumeratorArgs);
            var name = Identifier.New("GetEnumerator");
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.GetEmpty<Parameter>(), null, Sequence.GetEmpty<Parameter>());
            var typeName = Identifier.New(c.Name.Name + n.Name.Name);
            var body = GenerateEnumChildMethodBody(typeName);
            return ClassMemberDeclarator.NewExplicitMethodImpl(false, target, name, parameters, resultType, body);
        }

        private ClassMemberDeclarator GenerateEnumChildMethod(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var coll = CreateTypeReference("System.Collections.Generic.IEnumerable");
            var target = TypeExpression.NewBoundedGeneric(coll, Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference));
            var enumerator = CreateTypeReference("System.Collections.Generic.IEnumerator");
            var enumeratorArgs = Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference);
            var resultType = TypeExpression.NewBoundedGeneric(enumerator, enumeratorArgs);
            var name = Identifier.New("GetEnumerator");
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.GetEmpty<Parameter>(), null, Sequence.GetEmpty<Parameter>());
            var sys = TypeExpression.NewGlobalNameReference(Identifier.New("System"));
            var exc = TypeExpression.NewAccess(sys, Identifier.New("NotImplementedException"));
            var excArgs = ArgumentList.New(Sequence.GetEmpty<Argument>());
            var throwArg = Expression.NewNew(exc, excArgs);
            var throwStmt = Statement.NewThrow(throwArg);
            var statements = Sequence.Create(throwStmt);
            var block = Statement.NewBlock(statements);
            var suite = Statement.NewSuite(true, block);
            var body = ClassMethodBody.NewBlock(suite);
            return ClassMemberDeclarator.NewExplicitMethodImpl(false, target, name, parameters, resultType, body);
        }

        private ClassMemberDeclarator GenerateEnumChildExplicitMethodImpl(StructDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var resultType = CreateTypeReference("System.Collections.IEnumerator");
            var target = CreateTypeReference("System.Collections.IEnumerable");
            var name = Identifier.New("GetEnumerator");
            var cast = Expression.NewAsInstanceOf(Expression.NewThisReference(), _grammar.SyntaxTreeNodeInterfaceReference);
            var access = Expression.NewAccess(Expression.NewGroup(cast), name);
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.GetEmpty<Parameter>(), null, Sequence.GetEmpty<Parameter>());
            var body = ClassMethodBody.NewExpr(Expression.NewCall(access, ArgumentList.New(Sequence.GetEmpty<Argument>())));
            return ClassMemberDeclarator.NewExplicitMethodImpl(false, target, name, parameters, resultType, body);
        }

        private ClassMemberDeclarator GenerateEnumChildExplicitMethodImpl(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var resultType = CreateTypeReference("System.Collections.IEnumerator");
            var target = CreateTypeReference("System.Collections.IEnumerable");
            var name = Identifier.New("GetEnumerator");
            var cast = Expression.NewAsInstanceOf(Expression.NewThisReference(), _grammar.SyntaxTreeNodeInterfaceReference);
            var access = Expression.NewAccess(Expression.NewGroup(cast), name);
            var parameters = ParameterList.New(Sequence.GetEmpty<Parameter>(), Sequence.GetEmpty<Parameter>(), null, Sequence.GetEmpty<Parameter>());
            var body = ClassMethodBody.NewExpr(Expression.NewCall(access, ArgumentList.New(Sequence.GetEmpty<Argument>())));
            return ClassMemberDeclarator.NewExplicitMethodImpl(false, target, name, parameters, resultType, body);
        }
        #endregion

        #region ConvertTypeDeclarators
        private ReadOnlySet<string> GetSyntaxNodeValueTypes()
        {
            HashSet<string> set = new HashSet<string>();
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                switch (t.Value.Tag)
                {
                    case TypeDeclaratorCategory.Enum:
                        set.Add(t.Key);
                        break;
                    case TypeDeclaratorCategory.Struct:
                        if (t.Value.AsStruct.Declarator.IsValueType)
                            set.Add(t.Key);
                        break;
                    case TypeDeclaratorCategory.Union:
                        string name = t.Key + "Category";
                        set.Add(name);
                        break;
                    case TypeDeclaratorCategory.Visitor:
                    case TypeDeclaratorCategory.Interface:
                        break;
                    case TypeDeclaratorCategory.Class:
                        if (t.Value.AsClass.Declarator.Modifier == ClassVirtualModifier.Struct)
                            set.Add(t.Key);
                        break;
                    default:
                        throw new InvalidOperationException(SR.InvalidTypeDeclaratorCategory);
                }
            }
            return set.AsReadOnly();
        }

        private ReadOnlySet<string> GetSyntaxNodeTypes()
        {
            HashSet<string> set = new HashSet<string>();
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                switch (t.Value.Tag)
                {
                    case TypeDeclaratorCategory.Enum:
                        break;
                    case TypeDeclaratorCategory.Struct:
                        set.Add(t.Key);
                        break;
                    case TypeDeclaratorCategory.Union:
                        {
                            set.Add(t.Key);
                            var tags = GetUnionDeclaratorTags(t.Value.AsUnion.Declarator, true);
                            foreach (Identifier tag in tags)
                            {
                                string type = tag.Name + t.Key;
                                set.Add(type);
                            }
                            break;
                        }
                    case TypeDeclaratorCategory.Visitor:
                    case TypeDeclaratorCategory.Interface:
                    case TypeDeclaratorCategory.Class:
                        break;
                    default:
                        throw new InvalidOperationException(SR.InvalidTypeDeclaratorCategory);
                }
            }
            return set.AsReadOnly();
        }

        private Sequence<TypeDeclarator> ConvertSyntaxNodeTypes()
        {
            var decls = Sequence.GetEmpty<TypeDeclarator>();
            if (_grammar.UseSyntaxTreeNodeTypes)
            {
                var d = _grammar.SyntaxTreeNodeInterfaceDeclarator;
                var decl = TypeDeclarator.NewInterface(d.IsInternal, d.Name, d);
                decls = Sequence.Append(decls, decl);
            }
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                switch (t.Value.Tag)
                {
                    case TypeDeclaratorCategory.Enum:
                        decls = Sequence.Append(decls, t.Value);
                        break;
                    case TypeDeclaratorCategory.Struct:
                        {
                            var d = ConvertStructDeclarator(t.Value.AsStruct.Declarator);
                            var decl = TypeDeclarator.NewClass(d.IsInternal, d.Name, d);
                            decls = Sequence.Append(decls, decl);
                            break;
                        }
                    case TypeDeclaratorCategory.Union:
                        {
                            var e = GetUnionCategoryEnum(t.Value.AsUnion.Declarator);
                            var decl = TypeDeclarator.NewEnum(e.IsInternal, e.Name, e);
                            decls = Sequence.Append(decls, decl);
                            var classes = ConvertUnionDeclarator(t.Value.AsUnion.Declarator);
                            foreach (ClassDeclarator c in classes)
                            {
                                var item = TypeDeclarator.NewClass(c.IsInternal, c.Name, c);
                                decls = Sequence.Append(decls, item);
                            }
                            break;
                        }
                    case TypeDeclaratorCategory.Visitor:
                        {
                            var d = ConvertVisitorDeclarator(t.Value.AsVisitor.Declarator);
                            var decl = TypeDeclarator.NewInterface(d.IsInternal, d.Name, d);
                            decls = Sequence.Append(decls, decl);
                            break;
                        }
                    case TypeDeclaratorCategory.Interface:
                        decls = Sequence.Append(decls, t.Value);
                        break;
                    case TypeDeclaratorCategory.Class:
                        decls = Sequence.Append(decls, t.Value);
                        break;
                    default:
                        throw new InvalidOperationException(SR.InvalidTypeDeclaratorCategory);
                }
            }
            if (_grammar.UseSyntaxTreeNodeTypes)
            {
                var d = GetSyntaxTreeNodeWriterInterface();
                var decl = TypeDeclarator.NewInterface(d.IsInternal, d.Name, d);
                decls = Sequence.Append(decls, decl);
                var d2 = GetSyntaxTreeNodeWriterClass();
                var decl2 = TypeDeclarator.NewClass(d2.IsInternal, d2.Name, d2);
                decls = Sequence.Append(decls, decl2);
                var d3 = GetSyntaxTreeNodeWalkerInterface();
                var decl3 = TypeDeclarator.NewClass(d3.IsInternal, d3.Name, d3);
                decls = Sequence.Append(decls, decl3);
            }
            return decls;
        }

        private ReadOnlyDictionary<string, Sequence<string>> GetDerivedTypes()
        {
            var dict = new Dictionary<string, Sequence<string>>();
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                switch (t.Value.Tag)
                {
                    case TypeDeclaratorCategory.Enum:
                        dict.Add(t.Key, Sequence.GetEmpty<string>());
                        break;
                    case TypeDeclaratorCategory.Struct:
                        dict.Add(t.Key, Sequence.GetEmpty<string>());
                        break;
                    case TypeDeclaratorCategory.Union:
                        {
                            string name = t.Key + "Category";
                            dict.Add(name, Sequence.GetEmpty<string>());
                            UnionDeclarator n = t.Value.AsUnion.Declarator;
                            AddUnionDerivedTypes(n, n.Name.Name, n.Cases, dict);
                            break;
                        }
                    case TypeDeclaratorCategory.Visitor:
                    case TypeDeclaratorCategory.Interface:
                        break;
                    case TypeDeclaratorCategory.Class:
                        dict.Add(t.Key, Sequence.GetEmpty<string>());
                        break;
                    default:
                        throw new InvalidOperationException(SR.InvalidTypeDeclaratorCategory);
                }
            }
            return dict.AsReadOnly();
        }

        private void AddUnionDerivedTypes(UnionDeclarator n, string name, UnionCaseDeclaratorSuite suite, Dictionary<string, Sequence<string>> dict)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (suite == null)
                throw new ArgumentNullException(nameof(suite));
            if (dict == null)
                throw new ArgumentNullException(nameof(dict));
            var types = Sequence.GetEmpty<string>();
            var tags = GetUnionDeclaratorTags(suite, true);
            foreach (Identifier tag in tags)
            {
                string type = tag.Name + n.Name.Name;
                types = Sequence.Append(types, type);
            }
            dict.Add(name, types);
            foreach (UnionCaseDeclarator c in suite.Cases)
            {
                var typeName = c.Name.Name + n.Name.Name;
                if (c.Subcases == null)
                    dict.Add(typeName, Sequence.GetEmpty<string>());
                else
                    AddUnionDerivedTypes(n, typeName, c.Subcases, dict);
            }
        }

        private ReadOnlyDictionary<string, ReadOnlyCollection<VisitorDeclarator>> GetReferenceVisitors()
        {
            var dict = new Dictionary<string, List<VisitorDeclarator>>();
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                if (!t.Value.IsVisitor)
                    continue;
                var visitor = t.Value.AsVisitor.Declarator;
                foreach (TypeExpression typeRef in EnumUnionVariants(visitor.Parameters))
                {
                    if (!typeRef.IsNameReference)
                        continue;
                    string typeName = typeRef.AsNameReference.Name.Name;
                    if (!_nodeTypes.Contains(typeName))
                        continue;
                    dict.AddValueItem(typeName, visitor);
                }
            }
            return dict.AsReadOnly();
        }

        private Sequence<TypeExpression> EnumUnionVariants(ParameterList n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var seq = Sequence.GetEmpty<TypeExpression>();
            if (n.Positional.Count <= 0)
                return seq;
            var type = n.Positional[0].Type;
            if (!type.IsUnion)
                seq = Sequence.Append(seq, type);
            else
                seq = Sequence.Concat(seq, type.AsUnion.ElementTypes);
            return seq;
        }

        private ParameterList CreateNewVariant(ParameterList n, TypeExpression type)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (type == null)
                throw new ArgumentNullException(nameof(type));
            var first = n.Positional[0].WithType(type);
            var positional = n.Positional.RemoveAt(0);
            return n.WithPositional(Sequence.Prepend(first, positional));
        }

        private ParameterList CreateNewVariant(ParameterList n, Identifier v, TypeExpression type)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (v == null)
                throw new ArgumentNullException(nameof(v));
            if (type == null)
                throw new ArgumentNullException(nameof(type));
            var first = Parameter.New(v, type);
            var positional = n.Positional.RemoveAt(0);
            return n.WithPositional(Sequence.Prepend(first, positional));
        }

        private bool IsFieldChildNode(StructFieldDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.Type.IsNameReference && _nodeTypes.Contains(n.Type.AsNameReference.Name.Name))
                return true;
            if (n.Type.IsNullable)
            {
                TypeExpression t = n.Type.AsNullable.OperandType;
                if (t.IsNameReference && _nodeTypes.Contains(t.AsNameReference.Name.Name))
                    return true;
            }
            if (n.Type.IsList)
            {
                TypeExpression t = n.Type.AsList.ElementType;
                if (t.IsNameReference && _nodeTypes.Contains(t.AsNameReference.Name.Name))
                    return true;
            }
            if (n.Type.IsNonEmptyList)
            {
                TypeExpression t = n.Type.AsNonEmptyList.ElementType;
                if (t.IsNameReference && _nodeTypes.Contains(t.AsNameReference.Name.Name))
                    return true;
            }
            if (n.Type.IsMutableList)
            {
                TypeExpression t = n.Type.AsMutableList.ElementType;
                if (t.IsNameReference && _nodeTypes.Contains(t.AsNameReference.Name.Name))
                    return true;
            }
            if (n.Type.IsNonEmptyMutableList)
            {
                TypeExpression t = n.Type.AsNonEmptyMutableList.ElementType;
                if (t.IsNameReference && _nodeTypes.Contains(t.AsNameReference.Name.Name))
                    return true;
            }
            return false;
        }

        private Sequence<StructFieldDeclarator> GetTypeChildNodes(Sequence<StructFieldDeclarator> fields)
        {
            if (fields == null)
                throw new ArgumentNullException(nameof(fields));
            var children = Sequence.GetEmpty<StructFieldDeclarator>();
            foreach (StructFieldDeclarator f in fields)
                if (IsFieldChildNode(f))
                    children = Sequence.Append(children, f);
            return children;
        }

        private ReadOnlyDictionary<string, Sequence<StructFieldDeclarator>> GetChildNodes()
        {
            var dict = new Dictionary<string, Sequence<StructFieldDeclarator>>();
            foreach (var t in _grammar.SyntaxTreeNodeTypes)
            {
                switch (t.Value.Tag)
                {
                    case TypeDeclaratorCategory.Enum:
                        break;
                    case TypeDeclaratorCategory.Struct:
                        dict.Add(t.Key, GetTypeChildNodes(t.Value.AsStruct.Declarator.Fields));
                        break;
                    case TypeDeclaratorCategory.Union:
                        {
                            UnionDeclarator n = t.Value.AsUnion.Declarator;
                            AddUnionChildNodes(n, dict);
                            break;
                        }
                    case TypeDeclaratorCategory.Visitor:
                    case TypeDeclaratorCategory.Interface:
                    case TypeDeclaratorCategory.Class:
                        break;
                    default:
                        throw new InvalidOperationException(SR.InvalidTypeDeclaratorCategory);
                }
            }
            return dict.AsReadOnly();
        }

        private void AddUnionChildNodes(UnionDeclarator n, Dictionary<string, Sequence<StructFieldDeclarator>> dict)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (dict == null)
                throw new ArgumentNullException(nameof(dict));
            var queue = new Queue<Tuple<UnionCaseDeclaratorSuite, Sequence<StructFieldDeclarator>, Sequence<StructFieldDeclarator>>>();
            queue.Enqueue(Tuple.Create(n.Cases, Sequence.GetEmpty<StructFieldDeclarator>(), Sequence.GetEmpty<StructFieldDeclarator>()));
            while (queue.Count > 0)
            {
                var t = queue.Dequeue();
                var prefix = Sequence.Concat(t.Item2, t.Item1.PrefixFields);
                var postfix = Sequence.Concat(t.Item1.PostfixFields, t.Item3);
                foreach (UnionCaseDeclarator c in t.Item1.Cases)
                {
                    if (c.Subcases == null)
                    {
                        var type = c.Name.Name + n.Name.Name;
                        var fields = Sequence.Concat(prefix, Sequence.Concat(c.Fields, postfix));
                        fields = GetTypeChildNodes(fields);
                        dict.Add(type, fields);
                    }
                    if (c.Subcases != null)
                        queue.Enqueue(Tuple.Create(c.Subcases, Sequence.Concat(prefix, c.Fields), postfix));
                }
            }
        }

        private Identifier GetSyntaxTreeNodeWriterInterfaceName()
        {
            string name = _grammar.SyntaxTreeNodeInterfaceName;
            if (name.StartsWith("I"))
                name = name.Substring(1);
            return Identifier.New("I" + name + "Writer");
        }

        private Identifier GetSyntaxTreeNodeWriterClassName()
        {
            string name = _grammar.SyntaxTreeNodeInterfaceName;
            if (name.StartsWith("I"))
                name = name.Substring(1);
            return Identifier.New(name + "Writer");
        }

        private Identifier GetSyntaxTreeNodeWalkerClassName()
        {
            string name = _grammar.SyntaxTreeNodeInterfaceName;
            if (name.StartsWith("I"))
                name = name.Substring(1);
            return Identifier.New(name + "Walker");
        }

        private ClassDeclarator ConvertStructDeclarator(StructDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var i = n.IsInternal;
            var m = n.IsValueType ? ClassVirtualModifier.Struct : ClassVirtualModifier.Sealed;
            var b = Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference);
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var fields = n.Fields;
            if (_grammar.UseSourceContextField)
                fields = Sequence.Append(fields, _grammar.SourceConextField);
            var newMethodName = Identifier.New("New");
            var copyMethodName = Identifier.New("Copy");
            var withMethodNamePrefix = Identifier.New("With");
            var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + n.Name.Name);
            members = Sequence.Concat(members, GenerateBackingFields(fields));
            members = Sequence.Concat(members, GenerateProperties(fields));
            members = Sequence.Append(members, GenerateChildCountProperty(n));
            members = Sequence.Append(members, GenerateConstructor(true, fields));
            members = Sequence.Append(members, GenerateNewMethod(newMethodName, typeRef, fields, typeRef, false));
            members = Sequence.Append(members, GenerateCopyMethod(copyMethodName, typeRef, fields, typeRef));
            members = Sequence.Concat(members, GenerateWithMethods(withMethodNamePrefix, typeRef, fields, typeRef));
            members = Sequence.Append(members, GenerateGetChildIndexer(n));
            members = Sequence.Append(members, GenerateEnumChildMethod(n));
            members = Sequence.Append(members, GenerateEnumChildExplicitMethodImpl(n));
            // TODO: xhd: implement ToSyntax
            //members = Sequence.Concat(members, GenerateToSyntaxMethod(n.Name, fields, false));
            members = Sequence.Concat(members, GenerateToStringMethod(n.Name));
            members = Sequence.Concat(members, GenerateDefaultToStringMethod(n.IsValueType));
            var c = ClassDeclarator.New(i, m, n.Name, b, members, n.Context);
            return c;
        }

        private Sequence<ClassDeclarator> ConvertUnionCaseDeclarator(UnionDeclarator n, UnionCaseDeclarator c, TypeExpression baseType, Sequence<StructFieldDeclarator> prefixFields, Sequence<StructFieldDeclarator> postfixFields)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (c == null)
                throw new ArgumentNullException(nameof(c));
            if (baseType == null)
                throw new ArgumentNullException(nameof(baseType));
            if (prefixFields == null)
                throw new ArgumentNullException(nameof(prefixFields));
            if (postfixFields == null)
                throw new ArgumentNullException(nameof(postfixFields));
            var i = n.IsInternal;
            var m = c.Subcases == null ? ClassVirtualModifier.Sealed : ClassVirtualModifier.Abstract;
            var className = Identifier.New(c.Name.Name + n.Name.Name);
            var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + className.Name);
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            members = Sequence.Concat(members, GenerateBackingFields(c.Fields));
            members = Sequence.Concat(members, GenerateProperties(c.Fields));
            if (c.Subcases == null)
            {
                members = Sequence.Concat(members, GenerateIntrinsicProperties(n, c.Name));
                members = Sequence.Append(members, GenerateChildCountProperty(n, c));
            }
            members = Sequence.Append(members, GenerateConstructor(false, c.Fields, prefixFields, postfixFields));
            if (c.Subcases != null)
                members = Sequence.Concat(members, GenerateUnionNewMethods(n, c.Subcases, typeRef, Sequence.Concat(prefixFields, c.Fields), postfixFields, true));
            else
            {
                var newMethodName = Identifier.New("New");
                var copyMethodName = Identifier.New("Copy");
                var withMethodNamePrefix = Identifier.New("With");
                var allFields = Sequence.Concat(prefixFields, Sequence.Concat(c.Fields, postfixFields));
                members = Sequence.Append(members, GenerateNewMethod(newMethodName, typeRef, allFields, typeRef, false));
                members = Sequence.Append(members, GenerateCopyMethod(copyMethodName, typeRef, allFields, typeRef));
                members = Sequence.Concat(members, GenerateWithMethods(withMethodNamePrefix, typeRef, allFields, typeRef));
                members = Sequence.Append(members, GenerateGetChildIndexer(n, c));
                members = Sequence.Append(members, GenerateEnumChildMethod(n, c));
                members = Sequence.Concat(members, GenerateAcceptVisitorMethods(n, c));
                members = Sequence.Concat(members, GenerateAcceptWriterMethod(n, c));
                members = Sequence.Concat(members, GenerateAcceptWalkerMethod(n, c));
            }
            var classes = Sequence.GetEmpty<ClassDeclarator>();
            var bases = Sequence.Create(baseType);
            if (c.Subcases == null)
                bases = Sequence.Append(bases, _grammar.SyntaxTreeNodeInterfaceReference);
            var decl = ClassDeclarator.New(i, m, className, bases, members, n.Context);
            classes = Sequence.Append(classes, decl);
            if (c.Subcases != null)
            {
                prefixFields = Sequence.Concat(prefixFields, c.Fields);
                var decls = ConvertUnionCaseDeclaratorSuite(n, c.Subcases, typeRef, prefixFields, postfixFields);
                classes = Sequence.Concat(classes, decls);
            }
            return classes;
        }

        private Sequence<ClassMemberDeclarator> GenerateUnionNewMethods(UnionDeclarator n, UnionCaseDeclaratorSuite suite, TypeExpression thisTypeRef, Sequence<StructFieldDeclarator> prefixFields, Sequence<StructFieldDeclarator> postfixFields, bool isHiding)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (suite == null)
                throw new ArgumentNullException(nameof(suite));
            if (thisTypeRef == null)
                throw new ArgumentNullException(nameof(thisTypeRef));
            if (prefixFields == null)
                throw new ArgumentNullException(nameof(prefixFields));
            if (postfixFields == null)
                throw new ArgumentNullException(nameof(postfixFields));
            var list = Sequence.GetEmpty<Tuple<Identifier, Sequence<StructFieldDeclarator>>>();
            var queue = new Queue<Tuple<UnionCaseDeclaratorSuite, Sequence<StructFieldDeclarator>, Sequence<StructFieldDeclarator>>>();
            queue.Enqueue(Tuple.Create(suite, prefixFields, postfixFields));
            while (queue.Count > 0)
            {
                var t = queue.Dequeue();
                var prefix = Sequence.Concat(t.Item2, t.Item1.PrefixFields);
                var postfix = Sequence.Concat(t.Item1.PostfixFields, t.Item3);
                foreach (UnionCaseDeclarator c in t.Item1.Cases)
                {
                    if (c.Subcases == null)
                        list = Sequence.Append(list, Tuple.Create(c.Name, Sequence.Concat(prefix, Sequence.Concat(c.Fields, postfix))));
                    if (c.Subcases != null)
                        queue.Enqueue(Tuple.Create(c.Subcases, Sequence.Concat(prefix, c.Fields), postfix));
                }
            }
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            foreach (var t in list)
            {
                var newMethodName = Identifier.New("New" + t.Item1.Name);
                var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + t.Item1.Name + n.Name.Name);
                var decl = GenerateNewMethod(newMethodName, typeRef, t.Item2, thisTypeRef, isHiding);
                members = Sequence.Append(members, decl);
            }
            return members;
        }

        private Sequence<ClassDeclarator> ConvertUnionCaseDeclaratorSuite(UnionDeclarator n, UnionCaseDeclaratorSuite suite, TypeExpression baseType, Sequence<StructFieldDeclarator> prefixFields, Sequence<StructFieldDeclarator> postfixFields)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (suite == null)
                throw new ArgumentNullException(nameof(suite));
            if (baseType == null)
                throw new ArgumentNullException(nameof(baseType));
            if (prefixFields == null)
                throw new ArgumentNullException(nameof(prefixFields));
            if (postfixFields == null)
                throw new ArgumentNullException(nameof(postfixFields));
            prefixFields = Sequence.Concat(prefixFields, suite.PrefixFields);
            postfixFields = Sequence.Concat(suite.PostfixFields, postfixFields);
            var classes = Sequence.GetEmpty<ClassDeclarator>();
            foreach (UnionCaseDeclarator c in suite.Cases)
            {
                var decls = ConvertUnionCaseDeclarator(n, c, baseType, prefixFields, postfixFields);
                classes = Sequence.Concat(classes, decls);
            }
            return classes;
        }

        private Sequence<Identifier> GetUnionDeclaratorTags(UnionCaseDeclaratorSuite n, bool allowAbstract)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var list = Sequence.GetEmpty<Identifier>();
            var queue = new Queue<UnionCaseDeclaratorSuite>();
            queue.Enqueue(n);
            while (queue.Count > 0)
            {
                var suite = queue.Dequeue();
                foreach (UnionCaseDeclarator c in suite.Cases)
                {
                    if (allowAbstract || c.Subcases == null)
                        list = Sequence.Append(list, c.Name);
                    if (c.Subcases != null)
                        queue.Enqueue(c.Subcases);
                }
            }
            return list;
        }

        private Sequence<Identifier> GetUnionDeclaratorTags(UnionDeclarator n, bool allowAbstract)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return GetUnionDeclaratorTags(n.Cases, allowAbstract);
        }

        private EnumDeclarator GetUnionCategoryEnum(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var tags = GetUnionDeclaratorTags(n, false);
            var enumName = Identifier.New(n.Name.Name + "Category");
            var items = Sequence.GetEmpty<EnumItemDeclarator>();
            foreach (Identifier tag in tags)
            {
                var item = EnumItemDeclarator.New(Sequence.Create(tag));
                items = Sequence.Append(items, item);
            }
            var decl = EnumDeclarator.New(n.IsInternal, enumName, false, items);
            return decl;
        }

        private Sequence<ClassDeclarator> ConvertUnionDeclarator(UnionDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var i = n.IsInternal;
            var m = ClassVirtualModifier.Abstract;
            var b = Sequence.Create(_grammar.SyntaxTreeNodeInterfaceReference);
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var prefixFields = n.Cases.PrefixFields;
            var postfixFields = n.Cases.PostfixFields;
            if (_grammar.UseSourceContextField)
                postfixFields = Sequence.Append(postfixFields, _grammar.SourceConextField);
            var fields = Sequence.Concat(prefixFields, postfixFields);
            var tags = GetUnionDeclaratorTags(n, true);
            members = Sequence.Concat(members, GenerateBackingFields(fields));
            members = Sequence.Concat(members, GenerateProperties(fields));
            members = Sequence.Concat(members, GenerateIntrinsicProperties(n, tags));
            members = Sequence.Append(members, GenerateChildCountProperty(n));
            members = Sequence.Append(members, GenerateConstructor(false, fields));
            var typeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + n.Name.Name);
            prefixFields = Sequence.GetEmpty<StructFieldDeclarator>();
            postfixFields = Sequence.GetEmpty<StructFieldDeclarator>();
            if (_grammar.UseSourceContextField)
                postfixFields = Sequence.Append(postfixFields, _grammar.SourceConextField);
            members = Sequence.Concat(members, GenerateUnionNewMethods(n, n.Cases, typeRef, prefixFields, postfixFields, false));
            members = Sequence.Append(members, GenerateGetChildIndexer(n));
            members = Sequence.Append(members, GenerateEnumChildMethod(n));
            members = Sequence.Append(members, GenerateEnumChildExplicitMethodImpl(n));
            members = Sequence.Concat(members, GenerateAcceptVisitorMethods(n));
            members = Sequence.Concat(members, GenerateAcceptWriterMethod(n));
            members = Sequence.Concat(members, GenerateAcceptWalkerMethod(n));
            members = Sequence.Concat(members, GenerateToStringMethod(n.Name));
            members = Sequence.Concat(members, GenerateDefaultToStringMethod(false));
            var c = ClassDeclarator.New(i, m, n.Name, b, members, n.Context);
            var classes = Sequence.Create(c);
            var decls = ConvertUnionCaseDeclaratorSuite(n, n.Cases, typeRef, prefixFields, postfixFields);
            classes = Sequence.Concat(classes, decls);
            return classes;
        }

        private InterfaceDeclarator ConvertVisitorDeclarator(VisitorDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var members = Sequence.GetEmpty<InterfaceMemberDeclarator>();
            foreach (TypeExpression typeRef in EnumUnionVariants(n.Parameters))
            {
                if (!typeRef.IsNameReference)
                    continue;
                var typeName = typeRef.AsNameReference.Name.Name;
                var paramType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + typeName);
                var methodName = Identifier.New(n.MethodNamePrefix.Name + typeName);
                var parameters = CreateNewVariant(n.Parameters, paramType);
                var member = InterfaceMemberDeclarator.NewMethod(methodName, parameters, n.ResultType);
                members = Sequence.Append(members, member);
                Sequence<string> derivedTypes;
                if (_derivedTypes.TryGetValue(typeName, out derivedTypes))
                {
                    foreach (string derivedType in derivedTypes)
                    {
                        var derivedTypeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + derivedType);
                        var methName = Identifier.New(n.MethodNamePrefix.Name + derivedType);
                        var methParams = CreateNewVariant(n.Parameters, derivedTypeRef);
                        var meth = InterfaceMemberDeclarator.NewMethod(methName, methParams, n.ResultType);
                        members = Sequence.Append(members, meth);
                    }
                }
            }
            return InterfaceDeclarator.New(n.IsInternal, n.Name, Sequence.GetEmpty<TypeExpression>(), members);
        }
        #endregion

        #region SyntaxTreeNodeWriter
        private InterfaceDeclarator GetSyntaxTreeNodeWriterInterface()
        {
            var i = _grammar.SyntaxTreeNodeInterfaceIsInternal;
            var name = _syntaxTreeNodeWriterInterfaceName;
            var b = Sequence.GetEmpty<TypeExpression>();
            var members = Sequence.GetEmpty<InterfaceMemberDeclarator>();
            foreach (string nodeTypeName in _nodeTypes)
            {
                var nType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + nodeTypeName);
                var nName = Identifier.New("n");
                var nParam = Parameter.New(nName, nType);
                var wType = _grammar.IndentedWriterClassReference;
                var wName = Identifier.New("writer");
                var wParam = Parameter.New(wName, wType);
                var methodName = Identifier.New("Write" + nodeTypeName);
                var positionalOnly = Sequence.GetEmpty<Parameter>();
                var keywordOnly = Sequence.GetEmpty<Parameter>();
                var positional = Sequence.Create(nParam, wParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var method = InterfaceMemberDeclarator.NewMethod(methodName, parameters);
                members = Sequence.Append(members, method);
                if (nodeTypeName.EndsWith("Expression"))
                {
                    var dType = TypeExpression.NewInt();
                    var dName = Identifier.New("d");
                    var dParam = Parameter.New(dName, dType);
                    positional = Sequence.Create(nParam, wParam, dParam);
                    parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                    method = InterfaceMemberDeclarator.NewMethod(methodName, parameters);
                    members = Sequence.Append(members, method);
                }
            }
            var decl = InterfaceDeclarator.New(i, name, b, members);
            return decl;
        }

        private ClassDeclarator GetSyntaxTreeNodeWriterClass()
        {
            var i = _grammar.SyntaxTreeNodeInterfaceIsInternal;
            var m = ClassVirtualModifier.Default;
            var name = _syntaxTreeNodeWriterClassName;
            var baseTypeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterInterfaceName.Name);
            var b = Sequence.Create(baseTypeRef);
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            var thisTypeRef = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + _syntaxTreeNodeWriterClassName.Name);
            var instanceAccess = ClassMemberAccessibility.Public;
            var instanceName = Identifier.New("Instance");
            var instanceValue = Expression.NewNew(thisTypeRef, ArgumentList.New(Sequence.GetEmpty<Argument>()));
            var instance = ClassMemberDeclarator.NewPropertyField(instanceAccess, true, instanceName, false, thisTypeRef, instanceValue);
            members = Sequence.Append(members, instance);
            foreach (string nodeTypeName in _nodeTypes)
            {
                if (!nodeTypeName.EndsWith("Expression"))
                {
                    if (!_derivedTypes.ContainsKey(nodeTypeName) || _derivedTypes[nodeTypeName].Count <= 0)
                        continue;
                }

                var nType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + nodeTypeName);
                var nName = Identifier.New("n");
                var nParam = Parameter.New(nName, nType);
                var wType = _grammar.IndentedWriterClassReference;
                var wName = Identifier.New("writer");
                var wParam = Parameter.New(wName, wType);
                var access = ClassMemberAccessibility.Public;
                var modifier = ClassMemberVirtualModifier.Default;
                var methodName = Identifier.New("Write" + nodeTypeName);
                var positionalOnly = Sequence.GetEmpty<Parameter>();
                var keywordOnly = Sequence.GetEmpty<Parameter>();
                var actions = Sequence.GetEmpty<Statement>();
                actions = Sequence.Concat(actions, GenerateArgumentNullCheck(nParam));
                actions = Sequence.Concat(actions, GenerateArgumentNullCheck(wParam));

                var acceptMethodName = Identifier.New("Accept" + name.Name);
                var acceptMethodAccess = Expression.NewAccess(Expression.NewNameReference(nName), acceptMethodName);
                var acceptMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
                var acceptMethodArgW = Argument.NewPositional(Expression.NewNameReference(wName));
                if (!nodeTypeName.EndsWith("Expression"))
                {
                    var positional = Sequence.Create(nParam, wParam);
                    var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                    var acceptMethodArgs = ArgumentList.New(Sequence.Create(acceptMethodArgThis, acceptMethodArgW));
                    var acceptMethodCall = Expression.NewCall(acceptMethodAccess, acceptMethodArgs);
                    actions = Sequence.Append(actions, Statement.NewExpr(acceptMethodCall));
                    var block = Statement.NewBlock(actions);
                    var suite = Statement.NewSuite(true, block);
                    var body = ClassMethodBody.NewBlock(suite);
                    var method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, methodName, parameters, null, body);
                    members = Sequence.Append(members, method);
                }
                else
                {
                    var positional = Sequence.Create(nParam, wParam);
                    var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                    var writeMethodRef = Expression.NewNameReference(methodName);
                    var writeMethodArgN = Argument.NewPositional(Expression.NewNameReference(nName));
                    var writeMethodArgW = Argument.NewPositional(Expression.NewNameReference(wName));
                    var writeMethodArgD = Argument.NewPositional(Expression.NewInteger(IntegerLiteral.New(-1)));
                    var writeMethodArgs = ArgumentList.New(Sequence.Create(writeMethodArgN, writeMethodArgW, writeMethodArgD));
                    var writeMethodCall = Expression.NewCall(writeMethodRef, writeMethodArgs);
                    actions = Sequence.Append(actions, Statement.NewExpr(writeMethodCall));
                    var block = Statement.NewBlock(actions);
                    var suite = Statement.NewSuite(true, block);
                    var body = ClassMethodBody.NewBlock(suite);
                    var method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, methodName, parameters, null, body);
                    members = Sequence.Append(members, method);
                    if (_derivedTypes.ContainsKey(nodeTypeName) && _derivedTypes[nodeTypeName].Count > 0)
                    {
                        var dType = TypeExpression.NewInt();
                        var dName = Identifier.New("d");
                        var dParam = Parameter.New(dName, dType);
                        positional = Sequence.Create(nParam, wParam, dParam);
                        parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                        var acceptMethodArgD = Argument.NewPositional(Expression.NewNameReference(dName));
                        var acceptMethodArgs = ArgumentList.New(Sequence.Create(acceptMethodArgThis, acceptMethodArgW, acceptMethodArgD));
                        var acceptMethodCall = Expression.NewCall(acceptMethodAccess, acceptMethodArgs);
                        actions = Sequence.GetEmpty<Statement>();
                        actions = Sequence.Concat(actions, GenerateArgumentNullCheck(nParam));
                        actions = Sequence.Concat(actions, GenerateArgumentNullCheck(wParam));
                        actions = Sequence.Append(actions, Statement.NewExpr(acceptMethodCall));
                        block = Statement.NewBlock(actions);
                        suite = Statement.NewSuite(true, block);
                        body = ClassMethodBody.NewBlock(suite);
                        method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, methodName, parameters, null, body);
                        members = Sequence.Append(members, method);
                    }
                }
            }
            var decl = ClassDeclarator.New(i, m, name, b, members);
            return decl;
        }
        #endregion

        #region SyntaxTreeNodeWalker
        private ClassDeclarator GetSyntaxTreeNodeWalkerInterface()
        {
            var i = _grammar.SyntaxTreeNodeInterfaceIsInternal;
            var m = ClassVirtualModifier.Default;
            var name = _syntaxTreeNodeWalkerClassName;
            var b = Sequence.GetEmpty<TypeExpression>();
            var members = Sequence.GetEmpty<ClassMemberDeclarator>();
            foreach (string nodeTypeName in _nodeTypes)
            {
                var nType = CreateTypeReference(_grammar.SyntacticAnalysisNamespace + "." + nodeTypeName);
                var nName = Identifier.New("n");
                var nParam = Parameter.New(nName, nType);
                var access = ClassMemberAccessibility.Public;
                var modifier = ClassMemberVirtualModifier.Default;
                var positionalOnly = Sequence.GetEmpty<Parameter>();
                var keywordOnly = Sequence.GetEmpty<Parameter>();
                var positional = Sequence.Create(nParam);
                var parameters = ParameterList.New(positionalOnly, positional, null, keywordOnly);
                var actions = Sequence.GetEmpty<Statement>();
                if (NeedArgumentCheck(TypeExpression.NewNameReference(Identifier.New(nodeTypeName))))
                    actions = Sequence.Concat(actions, GenerateArgumentCheck(nParam));
                if (!_derivedTypes.ContainsKey(nodeTypeName) || _derivedTypes[nodeTypeName].Count <= 0)
                {
                    var block = Statement.NewBlock(actions);
                    var suite = Statement.NewSuite(true, block);
                    var enterLeaveMethodBody = ClassMethodBody.NewBlock(suite);
                    var enterMethodName = Identifier.New("Enter" + nodeTypeName);
                    var leaveMethodName = Identifier.New("Leave" + nodeTypeName);
                    var enterMethod = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, enterMethodName, parameters, null, enterLeaveMethodBody);
                    var leaveMethod = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, leaveMethodName, parameters, null, enterLeaveMethodBody);
                    members = Sequence.Append(members, enterMethod);
                    members = Sequence.Append(members, leaveMethod);
                    var enterMethodRef = Expression.NewNameReference(enterMethodName);
                    var leaveMethodRef = Expression.NewNameReference(leaveMethodName);
                    var enterLeaveMethodArgN = Argument.NewPositional(Expression.NewNameReference(nParam.Name));
                    var enterLeaveMethodArgs = ArgumentList.New(Sequence.Create(enterLeaveMethodArgN));
                    var enterMethodCall = Expression.NewCall(enterMethodRef, enterLeaveMethodArgs);
                    var leaveMethodCall = Expression.NewCall(leaveMethodRef, enterLeaveMethodArgs);
                    actions = Sequence.Append(actions, Statement.NewExpr(enterMethodCall));
                    if (_childNodes.ContainsKey(nodeTypeName))
                    {
                        var children = _childNodes[nodeTypeName];
                        foreach (StructFieldDeclarator field in children)
                        {
                            if (field.Type.IsNameReference)
                            {
                                var walkerName = Identifier.New("Walk" + field.Type.AsNameReference.Name.Name);
                                var walkerRef = Expression.NewNameReference(walkerName);
                                var walkerFieldRef = Expression.NewAccess(Expression.NewNameReference(nParam.Name), field.Name.ToUpperCase());
                                var walkerArgN = Argument.NewPositional(walkerFieldRef);
                                var walkerArgs = ArgumentList.New(Sequence.Create(walkerArgN));
                                var walkerCall = Expression.NewCall(walkerRef, walkerArgs);
                                actions = Sequence.Append(actions, Statement.NewExpr(walkerCall));
                            }
                            else if (field.Type.IsNullable)
                            {
                                var operandType = field.Type.AsNullable.OperandType.AsNameReference;
                                var walkerName = Identifier.New("Walk" + operandType.Name.Name);
                                var walkerRef = Expression.NewNameReference(walkerName);
                                var walkerFieldRef = Expression.NewAccess(Expression.NewNameReference(nParam.Name), field.Name.ToUpperCase());
                                var walkerArgN = Argument.NewPositional(walkerFieldRef);
                                var walkerArgs = ArgumentList.New(Sequence.Create(walkerArgN));
                                var walkerCall = Expression.NewCall(walkerRef, walkerArgs);
                                var fieldNullTestOp = RelationalOpValuePair.New(RelationalOp.IsNot, Expression.NewNull(NullLiteral.New()));
                                var fieldNullTestOps = Sequence.Create(fieldNullTestOp);
                                var fieldNullTest = Expression.NewRelational(walkerFieldRef, fieldNullTestOps);
                                var walkerNullCheck = Statement.NewIf(fieldNullTest, Statement.NewExpr(walkerCall), Sequence.GetEmpty<ElseIfBranch>());
                                actions = Sequence.Append(actions, walkerNullCheck);
                            }
                            else
                            {
                                NameReferenceTypeExpression elementType;
                                if (field.Type.IsList)
                                    elementType = field.Type.AsList.ElementType.AsNameReference;
                                else if (field.Type.IsNonEmptyList)
                                    elementType = field.Type.AsNonEmptyList.ElementType.AsNameReference;
                                else if (field.Type.IsMutableList)
                                    elementType = field.Type.AsMutableList.ElementType.AsNameReference;
                                else
                                    elementType = field.Type.AsNonEmptyMutableList.ElementType.AsNameReference;
                                var walkerName = Identifier.New("Walk" + elementType.Name.Name);
                                var walkerRef = Expression.NewNameReference(walkerName);
                                var walkerFieldRef = Expression.NewAccess(Expression.NewNameReference(nParam.Name), field.Name.ToUpperCase());
                                var forTarget = Identifier.New("child");
                                var forEnumerator = Expression.NewNameReference(forTarget);
                                var walkerArgN = Argument.NewPositional(forEnumerator);
                                var walkerArgs = ArgumentList.New(Sequence.Create(walkerArgN));
                                var walkerCall = Expression.NewCall(walkerRef, walkerArgs);
                                var walkerCallStmt = Statement.NewExpr(walkerCall);
                                var forStmt = Statement.NewFor(false, forEnumerator, walkerFieldRef, walkerCallStmt);
                                actions = Sequence.Append(actions, forStmt);
                            }
                        }
                    }
                    actions = Sequence.Append(actions, Statement.NewExpr(leaveMethodCall));
                    block = Statement.NewBlock(actions);
                    suite = Statement.NewSuite(true, block);
                    var body = ClassMethodBody.NewBlock(suite);
                    var methodName = Identifier.New("Walk" + nodeTypeName);
                    var method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, methodName, parameters, null, body);
                    members = Sequence.Append(members, method);
                }
                else
                {
                    var acceptMethodName = Identifier.New("Accept" + name.Name);
                    var acceptMethodAccess = Expression.NewAccess(Expression.NewNameReference(nName), acceptMethodName);
                    var acceptMethodArgThis = Argument.NewPositional(Expression.NewThisReference());
                    var acceptMethodArgs = ArgumentList.New(Sequence.Create(acceptMethodArgThis));
                    var acceptMethodCall = Expression.NewCall(acceptMethodAccess, acceptMethodArgs);
                    actions = Sequence.Append(actions, Statement.NewExpr(acceptMethodCall));
                    var block = Statement.NewBlock(actions);
                    var suite = Statement.NewSuite(true, block);
                    var body = ClassMethodBody.NewBlock(suite);
                    var methodName = Identifier.New("Walk" + nodeTypeName);
                    var method = ClassMemberDeclarator.NewVirtualMethod(access, false, modifier, methodName, parameters, null, body);
                    members = Sequence.Append(members, method);
                }
            }
            var decl = ClassDeclarator.New(i, m, name, b, members);
            return decl;
        }
        #endregion
    }
}
